Practice link: https://vjudge.net/problem/HDU-3376
题意:给你一个 n*n 的矩阵,每个点有自身的权值,从左上角向右下角走只能向右或向下,从右下角向左上角走只能向左或向上,问你从左上角走向右下角再从右下角走向左上角且每个点只能走一次,问你最大权值之和是多少。
思路:首先从右下角到左上角的过程可以转化为从左上角到右下角,那么问题就变成了从左上角到右下角的两条没有重复点的路径的最大权值和。然后看到是走矩阵且要求权值之和最大且每个格子只能走一次,就想到最大费用最大流。首先建立超级源点和超级汇点,源点到左上角的点的流量为2,费用为 0,右下角的点到汇点的流量为 2 ,费用为 0,由于每个点只能走一次,那么我们就进行拆点,即把点 k 拆成两个点 k1 和 k2,这两个点之间的流量为 1 ,费用为点 k 的权值(除了左上角和右下角的两个点的流量需要为 2)。其余可以联通的点之间的流量为 1 ,费用为 0 即可。
最后跑一遍最大费用最大流,又因为左上角和右下角的点经过了两次,最后答案要减去这两个点的权值。
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #include<string> 6 #include<vector> 7 #include<stack> 8 #include<bitset> 9 #include<cstdlib> 10 #include<cmath> 11 #include<set> 12 #include<list> 13 #include<deque> 14 #include<map> 15 #include<queue> 16 #define ll long long 17 #define MOD 998244353 18 #define INF 0x3f3f3f3f 19 #define mem(a,x) memset(a,x,sizeof(a)) 20 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 21 using namespace std; 22 23 const int maxn = 800000; 24 25 bool vis[maxn]; 26 int n,m,s,t; 27 int dis[maxn],pre[maxn],last[maxn],flow[maxn],maxflow,maxcost; 28 29 struct node{ 30 int to,nxt,flow,dis; //flow流量 dis花费 31 }edge[4*maxn]; 32 int head[4*maxn],cnt=0; 33 34 void addl(int u,int v,int flow,int dis) 35 { 36 edge[cnt].to=v; 37 edge[cnt].flow=flow; 38 edge[cnt].dis=dis; 39 edge[cnt].nxt=head[u]; 40 head[u]=cnt++; 41 } 42 43 bool spfa(int s,int t) 44 { 45 for(int i=0;i<=2*n*n+1;i++){ 46 dis[i]=-1; 47 flow[i]=INF; 48 vis[i]=0; 49 } 50 queue<int>q; 51 q.push(s); 52 vis[s]=1; 53 dis[s]=0; 54 pre[t]=-1; 55 while(!q.empty()){ 56 int now=q.front(); 57 q.pop(); 58 vis[now]=0; 59 for(int i=head[now];i!=-1;i=edge[i].nxt) 60 { 61 if(edge[i].flow>0&&dis[edge[i].to]<dis[now]+edge[i].dis) 62 { 63 dis[edge[i].to]=dis[now]+edge[i].dis; 64 pre[edge[i].to]=now; 65 last[edge[i].to]=i; 66 flow[edge[i].to]=min(flow[now],edge[i].flow); 67 if(!vis[edge[i].to]) 68 { 69 vis[edge[i].to]=1; 70 q.push(edge[i].to); 71 } 72 } 73 } 74 } 75 return pre[t]!=-1; 76 } 77 void MCMF() 78 { 79 while(spfa(s,t)) 80 { 81 int now=t; 82 maxflow+=flow[t]; 83 maxcost+=flow[t]*dis[t]; 84 while(now!=s) 85 { 86 edge[last[now]].flow-=flow[t]; 87 edge[last[now]^1].flow+=flow[t]; 88 now=pre[now]; 89 } 90 } 91 } 92 int mp[605][605]; 93 int main() 94 { 95 while (scanf("%d",&n)!=EOF) { 96 mem(head,-1); 97 mem(pre,-1); 98 cnt=0; 99 maxflow=0; 100 maxcost=0; 101 s=0; 102 t=2*n*n+1; 103 addl(s,1,2,0); 104 addl(1,s,0,0); 105 for(int i=1;i<=n;i++){ 106 for(int j=1;j<=n;j++){ 107 scanf("%d",&mp[i][j]); 108 int num=(i-1)*n+j; 109 if(i==j&&(num==1||num==n*n)){ 110 addl(num,num+n*n,2,mp[i][j]); 111 addl(num+n*n,num,0,-mp[i][j]); 112 }else{ 113 addl(num+n*n,num,0,-mp[i][j]); 114 addl(num,num+n*n,1,mp[i][j]); 115 } 116 if(i+1<=n){ 117 addl(num+n*n,i*n+j,1,0); 118 addl(i*n+j,num+n*n,0,0); 119 } 120 if(j+1<=n){ 121 addl(num+n*n,num+1,1,0); 122 addl(num+1,num+n*n,0,0); 123 } 124 } 125 } 126 addl(2*n*n,t,2,0); 127 addl(t,2*n*n,0,0); 128 MCMF(); 129 printf("%d ",maxcost-mp[1][1]-mp[n][n]); 130 } 131 return 0; 132 }