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; }
相关文章推荐
- 2017"百度之星"程序设计大赛 - 初赛(B) 度度熊的交易计划 最小费用最大流求最大费用
- zoj Rescue the Rabbit AC自动机+状态压缩DP n个有价值的子串,求长度为len的字符串的最大值(每子串的值最多用一次)
- 动态规划--二维费用背包--最大最小价值--hdu4968 Improving the GPA
- HDU 4160 最小路径覆盖 = 顶点数 - 最大匹配数 二分匹配
- Light OJ 1406 Assassin`s Creed 状态压缩DP+强连通缩点+最小路径覆盖
- (POJ 2253)Frogger 求所有可达路径中的最大边的最小值 dijkstra || floyd 变形
- 键盘输入3*3的整数矩阵,求两条对角线中最小的元素和最大的元素,使用普通指针变量实现。
- 矩阵从左上角到右下角的最优路径使得经过路径上的权值和最大(最小)
- 【BZOJ 3997】 3997: [TJOI2015]组合数学 (DP| 最小链覆盖=最大点独立集)
- poj 1422 Air Raid(最小路径覆盖 + 二分图最大匹配)
- 最小费用最大流
- 二分图最大匹配,最小路径覆盖,最小点覆盖,最大独立集,最小边覆盖与建图方法
- js得到最大的数 然后在循环select下列表一次递减到最小的数
- 图论中 [ 最小边覆盖/最小路径覆盖/最小顶点覆盖/最大独立集/最大团 ] 的概念与性质
- 【POJ 1651】【区间DP 矩阵链乘的变形】Multiplication Puzzle【一串数字,除了头尾不能动,每次取个数字,它与左右相邻数字的乘积为其价值,求价值和最小】
- [SDOI2010][bzoj1927] 星际竞速 [最小路径覆盖+费用流]
- Hdu 5314 Happy King(求树上多少个点对(u,v)满足u到v的路径上点权值最大值减最小值不大于给定的K)
- Console-算法[]-数组求最大值和最小值(只能遍历一次)
- hdu 6118 度度熊的交易计划 (最小费用最大流
- 二分图最大匹配与最小路径覆盖(一些题目)