最近看校招题发现搜狗2013的一道,程序员编程艺术第三十四格子取数问题出自于此,再来温习一遍。
dp方法
走个来回相当于从左上角到右下角走2次。
由于只能走右和下,所以可以以步数来划分状态。
0 1 2 3
1 2 3 4
2 3 4 5
3 4 5 6
第二组数据
3
10 3 3
2 5 3
6 7 10
可以转换为
10
2 3
6 5 3
* 7 3
* * 10
行为步数,[i][j]位置只能从[i-1][j],[i-1][j-1]过来。
dp[i][j][k] 表示第i步时取的2个数分别在j和k列时的最大值
转移方程为dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-1][k],dp[i-1][j][k-1],dp[i-1][j-1][k-1])
+map[i][j]+map[i][k];
#include<iostream>
using namespace std;
int map[1205][605];
int f[2][605][605];
int n,m;
inline int MAX(int a,int b)
{
return (a>b)? a:b;
}
int main()
{
int i,j,k;
while(scanf("%d",&n)!=EOF)
{
memset(map,-1,sizeof(map));
memset(f,-1,sizeof(f));
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d",&map[i+j-1][j]);
f[0][1][2]=map[1][1]+map[2][1]+map[2][2];
int flag=1;
m=2*n-2;
for(i=3;i<=m;i++)
{
j=i-n+1;
if(j<1) j=1;
for(;j<=i && j<=n;j++)
for(k=j+1;k<=i && k<=n;k++)
{
if(j==1)
f[flag][j][k]=MAX(f[1-flag][j][k],f[1-flag][j][k-1])
+map[i][j]+map[i][k];
else
f[flag][j][k]=MAX(MAX(f[1-flag][j][k],f[1-flag][j][k-1]),
MAX(f[1-flag][j-1][k],f[1-flag][j-1][k-1]))
+map[i][j]+map[i][k];
}
flag=!flag;
}
int ans=f[0][n-1][n]+map[2*n-1][n];
printf("%d\n",ans);
}
return 0;
}
费用流方法
将每个点拆为2个,新点和旧点连接流量1费用-map[i][j]的边
原来的点分别和它右下2个方向的新点连接流量1费用0的边
源点和左上角的点连接流量2费用0的边
右下角的新点和终点连接流量2费用0的边
求最小费用即可
#include <iostream>
using namespace std;
const int inf=0x3f3f3f3f;
struct rec
{
int u,v,w,c,link;
}edge[3000000];
int head[800000],pre[800000],dis[800000],Q[20000000];
bool vis[800000];
int map[605][605];
int n,num,st,ed,ans,maxflow,l,r;
inline void addedge(int u,int v,int w,int c)
{
edge[num].u=u;
edge[num].v=v;
edge[num].w=w;
edge[num].c=c;
edge[num].link=head[u];
head[u]=num++;
edge[num].u=v;
edge[num].v=u;
edge[num].w=0;
edge[num].c=-c;
edge[num].link=head[v];
head[v]=num++;
}
int fee_maxflow()
{
int ans=0,k=0,u,a,v,i;
while(1)
{
for(i=0;i<=ed;i++)
{
dis[i]=inf;
vis[i]=false;
}
dis[st]=0;
l=0;
r=1;
Q[0]=st;
pre[st]=0;
while(l!=r)
{
a=Q[l++];
vis[a]=false;
for(i=head[a];i;i=edge[i].link)
{
v=edge[i].v;
if(edge[i].w && dis[v]>dis[a]+edge[i].c)
{
dis[v]=dis[a]+edge[i].c;
pre[v]=i;
if(!vis[v])
{
Q[r++]=v;
vis[v]=true;
}
}
}
}
if(dis[ed]==inf)
break;
/*k=inf;
for(u=ed;pre[u];u=edge[pre[u]].u)
{
if(k>edge[pre[u]].w)
k=edge[pre[u]].w;
}*/
for(u=ed;pre[u];u=edge[pre[u]].u)
{
edge[pre[u]].w--;
edge[pre[u]^1].w++;
}
ans+=dis[ed];
}
return ans;
}
int main()
{
int i,j;
while(~scanf("%d",&n))
{
st=0;
ed=2*n*n-1;
memset(head,0,sizeof(head));
num=2;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
scanf("%d",&map[i][j]);
addedge(0, n * n, 2, 0);
for(i=0;i<n;i++)
for(j=0;j<n;j++)
{
if (i + 1 < n)
addedge(i * n + j + n * n, i * n + n + j, 1, 0);
if (j + 1 < n)
addedge(i * n + j + n * n, i * n + j + 1, 1, 0);
if (i == 0 && j == 0 || i == n - 1 && j == n - 1)
continue;
addedge(i * n + j, i * n + j + n * n, 1, -map[i][j]);
}
addedge(n * n - 1, n * n * 2 - 1, 2, 0);
/*for(i=2;i<num;i++)
cout<<i<<' '<<edge[i].u<<"-->"<<edge[i].v<<' '<<edge[i].c<<endl;*/
printf("%d\n",-fee_maxflow()+map[0][0]+map[n-1][n-1]);
}
return 0;
}