http://acm.split.hdu.edu.cn/showproblem.php?pid=2686
and
http://codevs.cn/problem/1169/
题意: 从矩阵左上角1,1,走到右下角n,n,每次只能向右或向下走,到达nn后,再走回1,1,往回走时,每次只能向左或者向上走,除了11和nn,其他点在去和回的路径中只能出现一次,问权值和最大的来回的路径是多少。
解法:找一条来回的路径,相当于从11到nn找两条点不重复的路径,使得两条的和最大。 相当于从源点 给11一个2的流量, nn给汇点一个n的流量, 跑最大流, 两个流经过的两条路就是要找的答案。 为了保证一个点只会被选择一次,考虑拆点。
把矩阵中的点xy,拆成两个点,一个称为in,一个称为out,所有指向这个点的边 连到in,所有从xy出发的边,从out点连出去, in-》out连流量为1,费用为- a【x】【y】,求最小费用最大流, 相反数就是最大权值和。其他的边就是往右和往下建边。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int inf=0x3f3f3f3f; 4 const int M=1e2+10; 5 class MaxFlowMinCost { ///最小费用最大流O(~=V^3) 6 typedef int typef;///流量的类型 7 typedef int typec;///费用的类型 8 static const int ME=1e6+10;///边的个数 9 static const int MV=5e3+10;///点的个数 10 queue<int> q; 11 int n,cur[MV],pre[MV]; 12 bool used[MV],sign[MV]; 13 typef flow; 14 typec cost,dist[MV]; 15 bool spfa(int s,int t) { 16 for(int i=0; i<=n; i++) { 17 used[i]=sign[i]=dist[i]=0; 18 } 19 used[s]=sign[s]=true; 20 while(!q.empty()) q.pop(); 21 q.push(s); 22 while(!q.empty()) { 23 // - 24 - 24 int u=q.front(); 25 q.pop(); 26 used[u]=false; 27 for(int i=g.head[u]; ~i; i=g.e[i].next) { 28 if(g.e[i].flow<1) continue; 29 int v=g.e[i].v; 30 typec c=g.e[i].cost; 31 if(!sign[v]||dist[v]>dist[u]+c) { 32 dist[v]=dist[u]+c; 33 sign[v]=true; 34 pre[v]=u; 35 cur[v]=i; 36 if(used[v]) continue; 37 used[v]=true; 38 q.push(v); 39 } 40 } 41 } 42 return sign[t]; 43 } 44 struct G { 45 struct E { 46 int v,next; 47 typef flow; 48 typec cost; 49 } e[ME]; 50 int le,head[MV]; 51 void init(int n) { 52 le=0; 53 for(int i=0; i<=n; i++) head[i]=-1; 54 } 55 void add(int u,int v,typef flow,typec cost) { 56 e[le].v=v; 57 e[le].flow=flow; 58 e[le].cost=cost; 59 e[le].next=head[u]; 60 head[u]=le++; 61 } 62 } g; 63 public: 64 void init(int tn) { 65 n=tn; 66 g.init(n); 67 } 68 // - 25 - 69 void add(int u,int v,typef flow,typec cost) { 70 g.add(u,v,flow,cost); 71 g.add(v,u,0,-cost); 72 } 73 void solve(int s,int t) { 74 flow=cost=0; 75 while(spfa(s,t)) { 76 int temp=t; 77 typef now=inf; 78 while(temp!=s) { 79 now=min(now,g.e[cur[temp]].flow); 80 temp=pre[temp]; 81 } 82 flow+=now; 83 temp=t; 84 while(temp!=s) { 85 int id=cur[temp]; 86 cost+=now*g.e[id].cost; 87 g.e[id].flow-=now; 88 g.e[id^1].flow+=now; 89 temp=pre[temp]; 90 } 91 } 92 } 93 typef getflow() { 94 return flow; 95 } 96 typec getcost() { 97 return cost; 98 } 99 } gx; 100 int a[M][M]; 101 int in[M][M]; 102 int out[M][M]; 103 int n,m; 104 int solve() { 105 int id=0; 106 for(int i=0; i<n; i++) { 107 for(int j=0; j<m; j++) { 108 in[i][j]=id; 109 id++; 110 } 111 } 112 for(int i=0; i<n; i++) { 113 for(int j=0; j<m; j++) { 114 out[i][j]=id; 115 id++; 116 } 117 } 118 int s=id; 119 id++; 120 int t=id; 121 id++; 122 gx.init(t); 123 gx.add(s,in[0][0],2,0); 124 gx.add(out[n-1][m-1],t,2,0); 125 for(int i=0; i<n; i++) { 126 for(int j=0; j<m; j++) { 127 int flow=1; 128 if(i==0&&j==0) flow=2; 129 if(i==n-1&&j==m-1) flow=2; 130 gx.add(in[i][j],out[i][j],flow,-a[i][j]); 131 if(i+1<n) { 132 gx.add(out[i][j],in[i+1][j],1,0); 133 } 134 if(j+1<m) { 135 gx.add(out[i][j],in[i][j+1],1,0); 136 } 137 } 138 } 139 gx.solve(s,t); 140 return -gx.getcost(); 141 } 142 int main() { 143 while(~scanf("%d",&n)) { 144 m=n; 145 for(int i=0; i<n; i++) { 146 for(int j=0; j<m; j++) { 147 scanf("%d",&a[i][j]); 148 } 149 } 150 printf("%d\n",solve()-a[0][0]-a[n-1][n-1]); 151 } 152 return 0; 153 }
有另一种动态规划解法,网上称之为多线程dp。
思想是,每一步让两条路同时向前走一格,状态dp【】【】【】【】记录了第一条路径终点在x1y1,第二条路径终点在x2y2这个状态的最大值,然后当两条路径走到同一个点,且这个点不是nm时,这种情况是不允许的,即可。因为两个路是同时走的,所以相撞的点只可能是终点,因为从11到某一个点xy需要的步数是定值(x-1)+(y-1)。转移有4个,因为每条路都有右下两种选法,两条路,所以有四种情况。
1 #include<bits/stdc++.h> 2 #define mt(a,b) memset(a,b,sizeof(a)) 3 using namespace std; 4 const int inf=0x3f3f3f3f; 5 const int M=32; 6 int n,m; 7 int a[M][M]; 8 int dp[M][M][M][M]; 9 int dfs(int x1,int y1,int x2,int y2){ 10 int &d=dp[x1][y1][x2][y2]; 11 if(~d) return d; 12 if(x1==x2&&y1==y2&&(x1!=n-1||y1!=m-1)&&(x1!=0||y1!=0)){ 13 d=-inf; 14 return d; 15 } 16 d=0; 17 if(x1>0&&x2>0){ 18 d=max(d,dfs(x1-1,y1,x2-1,y2)); 19 } 20 if(x1>0&&y2>0){ 21 d=max(d,dfs(x1-1,y1,x2,y2-1)); 22 } 23 if(y1>0&&x2>0){ 24 d=max(d,dfs(x1,y1-1,x2-1,y2)); 25 } 26 if(y1>0&&y2>0){ 27 d=max(d,dfs(x1,y1-1,x2,y2-1)); 28 } 29 d+=a[x1][y1]+a[x2][y2]; 30 return d; 31 } 32 int solve(){ 33 mt(dp,-1); 34 return dfs(n-1,m-1,n-1,m-1); 35 } 36 int main() { 37 while(~scanf("%d",&n)) { 38 m=n; 39 for(int i=0; i<n; i++) { 40 for(int j=0; j<m; j++) { 41 scanf("%d",&a[i][j]); 42 } 43 } 44 printf("%d\n",solve()-a[0][0]-a[n-1][m-1]); 45 } 46 return 0; 47 }
end