swjtu1583 用DP或最小费用最大流求一点至另一点两条路径的最大价值,同一格点算一次

/*
题意:给一n*n矩阵,找两条从(1,1)至(n,n)的路径,使得路径中的数的和最大,相同的位置最多只能算一次。
可用DP做,也可以用最小费用最大流做。其中网络流点有10^4,网络流不知道为什么0ms过。。。我原还担心会TLE,是数据弱了,还是算法非常高效,时间复杂度什么的是浮云。
最小费用最大流构图如下:将每个点一分为二,如果点i上有费用c,则建一条i1至i2容量为1,费用为-c的边
再建一条容量为1,费用为0的边。如果i点没有费用,则直接建一条i1至i2容量为2,费用为0的边即可。
至于只能向右一格和下一格移动,不妨设i能向j移动,则建一条i2至j1容量为2,费用为0的边。求最小费用最大流即可
DP:dp[i][j][k]表示经过k步,第一次横坐标为i,第二次横坐标为j的最大值。根据步数与横坐标可以知道纵坐标,所以此题就可以解决了
http://blog.csdn.net/magicnumber/article/details/6646190
*/
#include<stdio.h>
#include<iostream>
#include<string.h>
//#include<vector>
using namespace std;

const int maxn=21000;
const int maxint=0x3fffffff;
struct point
{
	int u,v,w,c,next;
}e[10*maxn];
int S,T,edgeNum,first[maxn],g[110][110],dis[maxn],v[maxn],slack[maxn],sum1,sum;//sum1最大容量,sum最小费用;

void Addedge(int u,int v,int w,int c)
{
	e[edgeNum].u=u,e[edgeNum].v=v,e[edgeNum].w=w,e[edgeNum].c=c,e[edgeNum].next=first[u],first[u]=edgeNum++;
	e[edgeNum].u=v,e[edgeNum].v=u,e[edgeNum].w=0,e[edgeNum].c=-c,e[edgeNum].next=first[v],first[v]=edgeNum++;
}

int Max(int a,int b)
{
	return a>b? a:b;
}
int Min(int a,int b)
{
	return a<b? a:b;
}
void Spfa()//用于处理重边
{
	int i,j,k,head=0,tail=0,q[maxn];
	for(i=S;i<=T;i++) dis[i]=maxint,v[i]=0;
	dis[S]=0,v[S]=1;
	q[tail++]=S;
	while(head!=tail)
	{
		v[k=q[head]]=0;
		head=(head+1)%maxn;
		for(i=first[k];i!=-1;i=e[i].next)
		{
			j=e[i].v;
			if(e[i].w>0&&dis[j]>dis[k]+e[i].c)
			{
				dis[j]=dis[k]+e[i].c;
				if(!v[j])
				{
					v[j]=1;
					q[tail]=j;
					tail=(tail+1)%maxn;
				}
			}
		}
	}
	for(i=S;i<=T;i++) dis[i]=dis[T]-dis[i];
}
int Augment(int x,int flow)
{
	if(x==T)
	{
		sum1+=flow;
		return flow;
	}
	int i,j,f=flow,delta=0,temp;

	v[x]=1;
	for(i=first[x];i!=-1;i=e[i].next)
	{
		j=e[i].v;
		if(e[i].w>0&&!v[j])
		{
			temp=dis[j]+e[i].c-dis[x];
			if(temp==0)
			{
				delta=Augment(j,Min(flow,e[i].w));
				if(delta>0)
				{
				   e[i].w-=delta;
				   e[i^1].w+=delta;
				   sum+=e[i].c*delta;
				   flow-=delta;
				   if(!flow) break;
				}
			}
			else if(temp<slack[j])
			   slack[j]=temp;
		}
	}
	return f-flow;
}
bool Adjust()
{
	int i,delta=maxint;
	for(i=S;i<=T;i++)
	{
		if(!v[i]&&slack[i]<delta)
			delta=slack[i];
		slack[i]=maxint;
	}
	if(delta==maxint) return false;
	for(i=S;i<=T;i++)
		if(v[i])
			dis[i]+=delta;
	return true;
}
void ZKWMinCMaxF()
{
	int i;
	sum=0,sum1=0;
	Spfa();//消负圈,视情况而定,有时可以不要的。。。
	for(i=S;i<=T;i++)
		slack[i]=maxint;
	do
	{
		memset(v,0,sizeof(v));
		while(Augment(S,maxint))
		   memset(v,0,sizeof(v));
	}while(Adjust());
}
int main()
{
	int n,i,j,x,y,w;
	while(scanf("%d",&n)!=EOF)
	{
		memset(g,0,sizeof(g));
		memset(first,-1,sizeof(first));
		edgeNum=0;
		while(1)
		{
			scanf("%d%d%d",&x,&y,&w);
			if(!x&&!y&&!w) break;
			x--,y--;
			g[x][y]=w;
		}
		for(i=0;i<n;i++)
			for(j=0;j<n;j++)
			{
				if(g[i][j])
				{
					Addedge(i*n+j,i*n+j+n*n,1,-g[i][j]);
					Addedge(i*n+j,i*n+j+n*n,1,0);
				}
				else
				{
					Addedge(i*n+j,i*n+j+n*n,2,0);
				}

				if(i!=n-1)
				{
					Addedge(i*n+j+n*n,(i+1)*n+j,2,0);
				}
				if(j!=n-1)
				{
					Addedge(i*n+j+n*n,i*n+j+1,2,0);
				}
			}
		S=0;
		T=2*n*n-1;
		ZKWMinCMaxF();
		printf("%d\n",-sum);
	}
	return 0;
}


 

你可能感兴趣的:(swjtu1583 用DP或最小费用最大流求一点至另一点两条路径的最大价值,同一格点算一次)