题意:给一个方阵,求从左上角出到右下角(并返回到起点),经过每个点一次不重复,求最大获益(走到某处获得改点数值),下来时每次只能向右或向下,反之向上或向左。
俩种解法:
1 费用流法:思路转化:从左上角流出2的流量,(表示走俩条路),归于右下角,可以走就有边(右和下),权为负的费用,源点连起点,汇点连终点,流量为2. 除源汇外所有点一分为2,Y向X对应点有流量1的边,之前边为X到Y的(原图),这样处理解决每个点只最多走一次(除了源汇外)(X部只出,Y部要出必先回到X对应点)。跑最小费用最大流即可。
2:dp法:(感谢XX大牛的提示)俩个点同时走,走了第K步状态:为x1,y1;x2,y2, 由于(x1+y1=k,x2+y2=k),状态压缩为3维,每个状态表示当前这步俩个点的横左边。
dp[k][x1][x2]=max(dp[k-1][x1][x2],dp[k-1][x1][x2-1],dp[k-1][x1-1][x2],dp[k-1][x1-1][x2-1])
方法1:
#include<cstdio> #include<iostream> #include<queue> #include<cstring> using namespace std; const int inf=0x3f3f3f3f; int a[50][50]; int nume=0;int e[50000][4];int head[2000]; int n; void inline adde(int i,int j,int c,int w) { e[nume][0]=j;e[nume][1]=head[i];head[i]=nume; e[nume][2]=c;e[nume++][3]=w; e[nume][0]=i;e[nume][1]=head[j];head[j]=nume; e[nume][2]=0;e[nume++][3]=-w; } int inq[2000];int pre[2000];int prv[2000]; int d[2000]; bool spfa(int &sum) { for(int i=0;i<=2*n*n+2;i++) { inq[i]=0; d[i]=inf; } queue<int>q; q.push(2*n*n); inq[2*n*n]=1; d[2*n*n]=0; while(!q.empty()) { int cur=q.front(); q.pop(); inq[cur]=0; for(int i=head[cur];i!=-1;i=e[i][1]) { int v=e[i][0]; if(e[i][2]>0&&d[cur]+e[i][3]<d[v]) { d[v]=d[cur]+e[i][3]; pre[v]=i; prv[v]=cur; if(!inq[v]) { q.push(v); inq[v]=1; } } } } if(d[2*n*n+1]==inf)return 0; int cur=2*n*n+1;int minf=inf; while(cur!=2*n*n) { minf=e[pre[cur]][2]<minf?e[pre[cur]][2]:minf; cur=prv[cur]; } cur=2*n*n+1; while(cur!=2*n*n) { e[pre[cur]][2]-=minf; e[pre[cur]^1][2]+=minf; cur=prv[cur]; } sum+=minf*d[2*n*n+1]; return 1; } int mincost() { int sum=0; while(spfa(sum)); return sum; } void init() { nume=0; memset(head,-1,sizeof(head)); } int main() { while(~scanf("%d",&n)) { init(); for(int i=0;i<n;i++) for(int j=0;j<n;j++) scanf("%d",&a[i][j]); for(int i=0;i<n;i++) for(int j=0;j<n;j++) { if(i+1<n) adde(i*n+j,(i+1)*n+j+n*n,1,-a[i+1][j]); if(j+1<n) adde(i*n+j,i*n+j+1+n*n,1,-a[i][j+1]); } for(int i=0;i<n*n;i++) { adde(i+n*n,i,1,0); } adde(2*n*n,0,2,0); adde(2*n*n-1,2*n*n+1,2,0); int ans=-mincost(); ans+=a[0][0]; ans-=a[n-1][n-1]; printf("%d ",ans); } return 0; }
方法2
;
#include<cstdio> #include<iostream> #include<cstring> using namespace std; int a[50][50]; int dp[80][50][50]; int max(int x,int y,int z,int t) { if(x>=y&&x>=z&&x>=t)return x; if(y>=x&&y>=z&&y>=t)return y; if(z>=x&&z>=y&&z>=t)return z; return t; } int main() { int n; while(~scanf("%d",&n)) { memset(dp,0,sizeof(dp)); for(int i=0;i<n;i++) for(int j=0;j<n;j++) scanf("%d",&a[i][j]); dp[0][0][0]=a[0][0]; dp[1][0][1]=a[0][1]+a[0][0]+a[1][0]; for(int k=1;k<=2*n-2;k++) { for(int x1=0;x1<=k&&x1<n;x1++) { for(int x2=0;x2<=k&&x2<n;x2++) { if(x1!=x2) { if(x1>0&&x2>0) dp[k][x1][x2]=max(dp[k-1][x1][x2],dp[k-1][x1][x2-1],dp[k-1][x1-1][x2],dp[k-1][x1-1][x2-1]); else if(x1==0&&x2>0) { dp[k][x1][x2]=max(dp[k-1][x1][x2],dp[k-1][x1][x2-1],0,0); } else { dp[k][x1][x2]=max(dp[k-1][x1][x2],dp[k-1][x1-1][x2],0,0); } dp[k][x1][x2]+=(a[x1][k-x1]+a[x2][k-x2]); } } } } int ans=dp[2*n-3][n-2][n-1]+a[n-1][n-1]; cout<<ans<<endl; } return 0; }