您的位置:首页 > 其它

[NOIP2009]最优贸易【spfa,dp,强联…

2015-01-19 20:41 134 查看
【问题描述】

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条道路的信息(每条道路所连接的两个城市的编号

以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。

【数据范围】

输入数据保证1号城市可以到达n号城市。

对于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。

解题思路:这道题的解法比较多,不过大多是基于动态规划的,,这里提两种算法。。

算法1:我们要维护的东西是从1走到i点时的最小价格和最大价格,,但是每个城市可以经过多次,,我们自然会想到强联通分量。求出强联通分量之后,缩点,对每一个强联通分量记录一个内部的最大价格和最小价格用于更新答案。。这样整个图就变成了一个有向无环图,我们利用拓扑序进行DP即可。。f[i]表示i点卖出的最大获利,min[i]表示走的i点的最小价格。。注意卖出点一定要在买入点之后,所以最大获利不一定是在n点卖出,所以我们取答案时要找每个点卖出的最大值,或者在DP过程中直接更新。。另外可能有一些点可以到达n但是从1出发到达不了(数据太弱没有这种情况),,这些点在拓扑DP之前要处理掉,,或者我们用记忆化搜索就可以避免这个问题了。。

算法2:对每个点维护一个minp[i],maxp[i],表示到达i点的最小和最大价格,我们用spfa来完成这个信息的更新。。同样卖出点一定要在买入点之后,所以我们先做一遍正向spfa更新最小,再做一遍反向spfa更新最大,这样就可以保证minp是在maxp的前面的了。。也可以像算法1那样,只做一遍正向spfa,只记录最小,最后取每个点(a[i]-minp[i])的最大值即可。。这个思想很有用,说明spfa可以消除动态规划的暂时后效性。

代码(算法1):

#include
#include
#include
#include
#include
using namespace std;
struct point{
int e;
point *next;
};
struct edge{
int s,e;
};
point *a[200005],*p;
intsuml=0,i,n,m,s,e,t,d=0,top=0,dui[100005],xu[100005],minc[100005],cost[100005],mincost[200005],maxcost[200005],u[100005],stack[100005],instack[100005],f[200005],ans=0;
edge ed[100005];
void addedge(int s,int e)
{point *p;
p=(point*)malloc(sizeof(point));
p->e=e;
p->next=a[s];
a[s]=p;
}
void tarjan(int k)
{xu[k]=minc[k]=++d;
u[k]=1;
stack[++top]=k;
instack[k]=1;
point *p;
p=(point*)malloc(sizeof(point));
p=a[k];
while (p!=0)
{if (u[p->e]==0) tarjan(p->e);
if (instack[p->e]==1) minc[k]=min(minc[k],minc[p->e]);
p=p->next;
}
if (minc[k]==xu[k])
{suml++;
while (stack[top]!=k)
{dui[stack[top]]=suml+n;
instack[stack[top]]=0;
mincost[suml+n]=min(mincost[suml+n],cost[stack[top]]);
maxcost[suml+n]=max(maxcost[suml+n],cost[stack[top]]);
top--;
}
dui[k]=suml+n;
instack[k]=0;
mincost[suml+n]=min(mincost[suml+n],cost[k]);
maxcost[suml+n]=max(maxcost[suml+n],cost[k]);
top--;
}
}
void dfs(int k,int minn,int get)
{u[k]=1;
minn=min(mincost[k],minn);
get=max(get,maxcost[k]-minn);
if (k==dui
) {ans=max(ans,get);return;}
point *p;
p=(point*)malloc(sizeof(point));
p=a[k];
while (p!=0)
{if (u[p->e]==0) dfs(p->e,minn,get);
p=p->next;
}
}
bool cmp(edge a,edge b)
{if (a.s!=b.s) return a.s
}
int main()
{freopen("trade.in","r",stdin);
scanf("%d%d",&n,&m);
for (i=1;i<=n;i++)
{scanf("%d",&cost[i]);
a[i]=0;
dui[i]=i;
}
for (i=1;i<=m;i++)
{scanf("%d%d%d",&s,&e,&t);
addedge(s,e);
if (t==2) addedge(e,s);
}
memset(u,0,sizeof(u));
memset(stack,0,sizeof(instack));
memset(mincost,1,sizeof(mincost));
memset(maxcost,0,sizeof(maxcost));
tarjan(1);
int fi,fe;
p=(point*)malloc(sizeof(point));
memset(ed,0,sizeof(ed));int sume=0;
for (i=1;i<=n;i++)
{p=a[i];
while (p!=0)
{if (dui[i]!=dui[p->e]){ed[++sume].s=dui[i];ed[sume].e=dui[p->e];}
p=p->next;
}
}
sort(ed+1,ed+1+sume,cmp);
for (i=1;i<=sume;i++)
{while (ed[i].s==ed[i+1].s&&ed[i].e==ed[i+1].e) i++;
addedge(ed[i].s,ed[i].e);
}
memset(u,0,sizeof(u));
dfs(dui[1],10000000,0);
printf("%d",ans);
fclose(stdin);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: