这道题目和poj3422差不多 ---->>>>>点击打开链接
给出一个矩阵,每个点都有一个权值。找出一条路径:从最左上角的点到最右下角的点,再从最右下角的点到最左上角的点,使途中经过的点的权值总和最大。并且除了最左上角和最右下角的点,每个点只能经过一次,最左上角的点和最右下角的点的权值只算一次。
思路:
拆点,把矩阵里的每一个点拆成两个点为x和x'。每个x连一条边到x',费用为该点的权值,容量为1。
每个点的x'向右面和下面的点y各连一条边,费用为0,容量为1。
源点到1的容量inf,费用为0,n×n的点到汇点容量为inf,费用为0;
1到1'和2×n×n到汇点 再连一条费用为0,容量为inf的边
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
struct node
{
int u,v,w,f,next;
} edge[50000];
int s,T,cnt;
int head[55000],pre[55000],vis[50500],dis[55000];
int map[1000][1000];
void add(int u,int v,int w,int f)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].f=f;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].u=v;
edge[cnt].v=u;
edge[cnt].w=0;
edge[cnt].f=-f;
edge[cnt].next=head[v];
head[v]=cnt++;
}
int SPFA()
{
int i;
memset(pre,-1,sizeof(pre));
memset(vis,0,sizeof(vis));
for(i=0; i<=cnt; i++)
dis[i]=-1;
queue<int>q;
dis[s]=0;
vis[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
i=head[u];
vis[u]=0;
while(i!=-1)
{
if(edge[i].w>0&&dis[edge[i].v]<dis[u]+edge[i].f)
{
dis[edge[i].v]=dis[u]+edge[i].f;
pre[edge[i].v]=i;
if(!vis[edge[i].v])
{
vis[edge[i].v]=1;
q.push(edge[i].v);
}
}
i=edge[i].next;
}
}
if(pre[T]==-1)
return 0;
return 1;
}
int MincostMaxFlow()
{
int ans=0;
while(SPFA())
{
int maxl=INF;
int p=pre[T];
while(p!=-1)
{
maxl=min(maxl,edge[p].w);
p=pre[edge[p].u];
}
p=pre[T];
while(p!=-1)
{
edge[p].w-=maxl;
edge[p^1].w+=maxl;
ans+=maxl*edge[p].f;
p=pre[edge[p].u];
}
}
return ans;
}
int main()
{
int n;
int a,b;
while(~scanf("%d",&n))
{
cnt=0;
memset(head,-1,sizeof(head));
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
scanf("%d",&map[i][j]);
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
{
a=(i-1)*n+j;
b=a+n*n;
add(a,b,1,map[i][j]);
add(a,b,INF,0);
if(i<n)
add(b,a+n,2,0);
if(j<n)
add(b,a+1,2,0);
}
s=0;
T=2*n*n+1;
add(s,1,2,0);
add(2*n*n,T,2,0);
int ans=0;
ans+=MincostMaxFlow();
printf("%d\n",ans);
}
return 0;
}