ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【NOIP2009提高组】最优贸易

2019-11-16 17:52:47  阅读:285  来源: 互联网

标签:maxn int 城市 NOIP2009 道路 贸易 最优 include 水晶球


Description

  C 国有n 个大城市和m 条道路,每条道路连接这n 个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为1 条。
  C 国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。
  商人阿龙来到 C 国旅游。当他得知同一种商品在不同城市的价格可能会不同这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚回一点旅费。设C 国n 个城市的标号从1~ n,阿龙决定从1 号城市出发,并最终在n 号城市结束自己的旅行。在旅游的过程中,任何城市可以重复经过多次,但不要求经过所有n 个城市。阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品――水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。由于阿龙主要是来C 国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。
  假设 C 国有5 个大城市,城市的编号和道路连接情况如下图,单向箭头表示这条道路为单向通行,双向箭头表示这条道路为双向通行。
  
  假设 1~n 号城市的水晶球价格分别为4,3,5,6,1。
  阿龙可以选择如下一条线路:1->2->3->5,并在2 号城市以3 的价格买入水晶球,在3号城市以5 的价格卖出水晶球,赚取的旅费数为2。
  阿龙也可以选择如下一条线路 1->4->5->4->5,并在第1 次到达5 号城市时以1 的价格买入水晶球,在第2 次到达4 号城市时以6 的价格卖出水晶球,赚取的旅费数为5。
  现在给出 n 个城市的水晶球价格,m 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。

Input

  第一行包含 2 个正整数n 和m,中间用一个空格隔开,分别表示城市的数目和道路的数目。  第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这n 个城市的商品价格。  接下来 m 行,每行有3 个正整数,x,y,z,每两个整数之间用一个空格隔开。如果z=1,表示这条道路是城市x 到城市y 之间的单向道路;如果z=2,表示这条道路为城市x 和城市y 之间的双向道路。

Output

  包含1 个整数,表示最多能赚取的旅费。如果没有进行贸易,则输出0。

Sample Input 1 

5 5
4 3 5 6 1
1 2 1
1 4 1
2 3 2
3 5 1
4 5 2

Sample Output 1

5

Hint

对于 10%的数据,1≤n≤6。 对于 30%的数据,1≤n≤100。 对于 50%的数据,不存在一条旅游路线,可以从一个城市出发,再回到这个城市。 对于 100%的数据,1≤n≤100000,1≤m≤500000,1≤x,y≤n,1≤z≤2,1≤各城市水晶球价格≤100。    
    法一: 分析可知我们只关心在哪里买,在哪里卖,而不关心如何到达这两个点。 因此找到能到起点且能到终点的点,在这样的点里面 找费用最小和最大的分别就是买入点和卖出点   标记点用BFS,找到终点的点建反图即可 法一清晰易懂
#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 100005, maxm = 500005, inf = 0x3f3f3f3f;

vector<int> g[maxn], bg[maxn];
void add(int x, int y) {
    g[x].push_back(y);
    bg[y].push_back(x);
}
int w[maxn];

void bfs(int s, int* vis, vector<int> *G) {
    queue<int> Q;
    Q.push(s);
    memset(vis, 0, sizeof(vis));
    
    while(!Q.empty()) {
        int u=Q.front(); Q.pop();
        for(int i=0; i<G[u].size(); i++) {
            int v = G[u][i];
            if(!vis[v]) vis[v] = 1, Q.push(v);
        }
    }
}

int r1[maxn], r2[maxn];
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++) scanf("%d", &w[i]);
    while(m--) {
        int a, b, p;
        scanf("%d%d%d", &a, &b, &p);
        add(a, b);
        if(p-1) add(b, a);
    }
    bfs(1, r1, g);
    bfs(n, r2, bg);
    int ans1 = inf, ans2 = 0;
    for(int i=1; i<=n; i++) {
        if(r1[i] && r2[i])
            ans1 = min(ans1, w[i]),
            ans2 = max(ans2, w[i]);
    }
    printf("%d", ans2 - ans1);
    return 0;
}
View Code

 

法二:洛谷上看到的做法,思路有点奇特

分层图+SPFA

考虑某个点 i ,它买入或者卖出水晶球的花费是v[i] 。

当我们进行买入操作,我们就建立一条有向边转移到一张新图上,边的大小为-v[i],指向点i所能到达的点(在第二层图上)而这张新图就是我们的第二层图。

它表示:假如我选择走了这条边,就是我在这个点买了这个水晶球,我不会反悔,并且我接下来考虑在某个点卖它。

当我们进行卖出操作,我们建立一条有向边转移到第三层图上,边的大小为v[i],指向i所能到达的点(在第三层图上)。

它表示:假如我选择走了这条边,就是我在这个点卖了这个水晶球,我不会反悔,并且我接下来考虑走向终点。

可以发现,从第一层图走到第二层图走到第三层图走到终点,这就是一个合法的选择,而且分层图把所有可能的决策都考虑到了。

最后走向终点,我们有两种合法的操作:

  • 不买卖直接走向终点

直接在第一层图的n号节点建立边权为0的有向边接入一个“超级终点”

  • 买卖一次后走向终点

在第三层图的n号节点建立边权为0的有向边接入“超级终点”

最后解释一下为什么我们要分层

因为当你分了层,你就可以从还未买入这个状态,转移到已经买入准备卖出这个状态,然后在转移到从卖出点走向终点的状态。由于有向边的建立,你不能从第二/三层走回第一层图,这保证了你只做一次买卖,而不是无限做买卖,符合了题目的要求

而我们最终的答案,就是求从第一层图的1号点,经过三层图走到“超级终点”的最长路,如图所示。

(小声bb:实测这种做法空间时间都是法一的两倍左右,因此权当一种思维拓展)

附上自己实现的丑陋代码:

#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 100005, maxm = 500005, inf = 0x3f3f3f3f;

int n;
struct edge{ int to, w; };
vector<edge> g[maxn*3];
int w[maxn];
void add(int x, int y, int z) {
    g[x].push_back((edge){y, 0});
    g[x+n].push_back((edge){y+n, 0});
    g[x+n*2].push_back((edge){y+n*2, 0});
    g[x].push_back((edge){y+n, -w[x]});
    g[x+n].push_back((edge){y+n*2, w[x]});
}
int dis[maxn*3];
int inq[maxn*3];
void bfs(int s) {
    queue<int> Q;
    Q.push(s); inq[s] = 1;
    for(int i=1; i<=n*3+1; i++) dis[i] = -inf;
    dis[s] = 0;
    
    while(!Q.empty()) {
        int u=Q.front(); Q.pop();
        inq[u] = 0;
        for(int i=0; i<g[u].size(); i++) {
            int v = g[u][i].to, p = g[u][i].w;
            if(dis[v] < dis[u] + p) {
                dis[v] = dis[u] + p;
                if(!inq[v]) Q.push(v), inq[v] = 1;
            }
        }
    }
}

int main() {
    int m;
    scanf("%d%d", &n, &m);
    g[n].push_back((edge){n*3+1, 0});
    g[n*3].push_back((edge){n*3+1, 0});
    for(int i=1; i<=n; i++)
        scanf("%d", &w[i]);
    while(m--) {
        int a, b, p;
        scanf("%d%d%d", &a, &b, &p);
        add(a, b, p);
        if(p-1) add(b, a, p);
    }
    bfs(1);
    printf("%d", dis[n*3+1]);
    return 0;
}
View Code

标签:maxn,int,城市,NOIP2009,道路,贸易,最优,include,水晶球
来源: https://www.cnblogs.com/de-compass/p/11872695.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有