[Wikioi 1173][NOIP 2009提高组]最优贸易(疑难题)

妈的一直80分,调三四个小时才AC,蛋疼啊,卧草草草

用SPFA做,和其他题不同,这里不求最短路,而是路径上的结点最小值/最大值,最后起点到终点间的结点最大值减去最小值就是结果

#include <stdio.h>
#include <string.h>
#define MAXN 100001
#define INF 10000000
struct LINE //保存边的结构体 
{
   int from; //边的起点 
   int to; //边的终点 
   int Last; //上一条边的编号 
   int Next; //下一条边的编号 
}ln[MAXN*10];
int nLine=0,n,m; //nLine记录边的个数 
int price[MAXN]; //price[i]=第i个点的价格 
int maxSold[MAXN]; //sold[i]=到达点i的最大卖出价
int minBuy[MAXN]; //minBuy[i]=到达点i的最小买入价
int que[MAXN*10]; //数组que保存队列 
int visit[MAXN*10]; //visit[i]=1表示第i个点已经访问过了,visit[i]=0表示第i个点没访问过
int lastLine[MAXN]; //lastLine[i]=以i为终点的边的编号
int nextLine[MAXN]; //nextLine[i]=以i为起点的边的编号
int min(int a,int b)
{
	if(a<b) return a;
	return b;
}
int max(int a,int b)
{
	if(a>b) return a;
	return b;
}
void insert(int u,int v) //建立边u->v
{
   nLine++;
   ln[nLine].from=u;
   ln[nLine].to=v;
   ln[nLine].Last=lastLine[u];
   ln[nLine].Next=nextLine[v]; 
   lastLine[u]=nLine;
   nextLine[v]=nLine;
} 
void SPFA1() //第一次SPFA,从起点到终点找出到达点i时的,路途上的最小买入价
{
	int head=0,tail=0; //初始时,队列的队首、队尾均为0
	que[0]=1;
	minBuy[1]=price[1];
	visit[1]=1;
	//初始时,队列的队首为第1个点(队首位于0),到达第1个点时的最小买入价为第1个点的价格,标记第1个点访问过
	while(head<=tail) //队列不为空
	{
		int p=lastLine[que[head]]; //p=队首点对应编号
		while(p>0)
		{
			if(minBuy[ln[p].to]>minBuy[que[head]]||price[ln[p].to]<minBuy[ln[p].to]) //松弛获得更优解
			{
				minBuy[ln[p].to]=min(minBuy[que[head]],price[ln[p].to]);
				if(!visit[ln[p].to]) que[++tail]=ln[p].to; //将更优解入队
				visit[ln[p].to]=1; //标记该点已经访问过
			}
			p=ln[p].Last; //开始访问与该边连接的下一边
		}
		visit[que[head]]=0;
		head++; //队首的点处理完成,队首出队
	}
} 
//下面的第二次SPFA与第一次的大同小异,只不过起点变成了终点,求最小解变成最大解,从一个边起点开始变成从一个边终点开始
void SPFA2() //第二次SPFA,从终点到起点找出到达点i时的最大卖出价 
{
	//很多数组在第一次SPFA中用过了,重置它们
	memset(visit,0,sizeof(visit));
	memset(maxSold,-1,sizeof(maxSold));
	int head=0,tail=0; //初始时,队列的队首、队尾均为0
	que[0]=n;
	maxSold[n]=price[n];
	visit[n]=1;
	//初始时,队列的队首为第n个点(队首位于0),到达第n个点时的最大卖出价为第n个点的价格,标记第n个点访问过
	while(head<=tail) //队列不为空
	{
		int p=nextLine[que[head]]; //p=队首点对应编号
		while(p>0) //由队首点为终点的边开始,搜索完所有联通的点
		{
			if(maxSold[ln[p].from]<maxSold[que[head]]||price[ln[p].from]>maxSold[ln[p].from]) //松弛获得更优解
			{
				maxSold[ln[p].from]=max(maxSold[que[head]],price[ln[p].from]);
				if(!visit[ln[p].from]) que[++tail]=ln[p].from; //如果没有访问过,将更优解入队
				visit[ln[p].from]=1; //标记该点已经访问过
			}
			p=ln[p].Next; //开始访问与该边连接的上一边
		}
		visit[que[head]]=0;
		head++; //队首的点处理完成,队首出队
	}
} 
int main()
{
   int i,j,x,y,z,ans=0;
   memset(minBuy,127,sizeof(minBuy));
   scanf("%d%d",&n,&m);
   for(i=1;i<=n;i++)
      scanf("%d",&price[i]);
   for(i=1;i<=m;i++)
   {
      scanf("%d%d%d",&x,&y,&z);
      insert(x,y); //建立边x->y
      if(z==2)
         insert(y,x); //如果是无向边,再建立边y->x
   }
   SPFA1();
   SPFA2();
   for(i=1;i<=n;i++)
      ans=max(maxSold[i]-minBuy[i],ans);
   printf("%d",ans);
   return 0;
}


 

你可能感兴趣的:(图论,SPFA)