您的位置:首页 > 其它

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

2011-07-28 17:49 399 查看
/*
题意:给一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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐