题意
有一个矩阵,问从左上角走到右下角(只能往右或者往下走),再从右下角回到左上角(只能往上或者往左),每个点只能走一次(除了左上角和右下角),问所走的格子的最大总和是多少。
思路
因为是矩阵形式,所以用拆点的方法,同一个点拆成两个,容量为1,因为容量为1所以每个点只能流过一次。
因为除了源点和汇点可以走两次外,其他点只能走一次,所以左上角的源点和右下的汇点容量为2,表示可以流过两次。
其他点之间容量为1。
如何建边?
tot=-1,s=1,t=2*n*n;
memset(head,-1,sizeof(head));
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
scanf("%d",&a[i][j]);
add((i-1)*n+j, (i+n-1)*n+j,1,-a[i][j]);//cap,cost
if(i!=n)
add((i+n-1)*n+j,i*n+j,1,0);
if(j!=n)
add((i+n-1)*n+j,(i-1)*n+j+1,1,0);
}
}
add(s,n*n+1,1,0);//源点到起点,容量2,费用0
add(n*n,t,1,0);//终点到汇点,容量2,费用0
剩下部分代码套板子就行。
注意
对于数组需要开多大,考虑一下,我错了十几次。
AC代码
#include<stdio.h>
#include<iostream>
#include<queue>
#include<string.h>
using namespace std;
#define inf 0x3f3f3f3f
const int N=660;
bool book[N*N*2];//无*2 TLE
int n,m,tot,s,t,a[N][N],pre[N*N*2],head[N*N*2],dist[N*N*2];//无*2 RT
struct node
{
int v,nextt,cap,flow,cost;
} e[N*N*8];
void add(int u,int v,int cap,int cost)
{
tot++;
e[tot].v=v;
e[tot].nextt=head[u];
e[tot].cap=cap;
e[tot].cost=cost;
e[tot].flow=0;//
head[u]=tot;
tot++;
e[tot].v=u;
e[tot].nextt=head[v];
e[tot].cap=0;
e[tot].cost=-cost;
e[tot].flow=0;//
head[v]=tot;
}
//queue<int>Q;
bool SPFA()
{
for(int i=0; i<=t; i++)
{
dist[i]=inf;
book[i]=0;
pre[i]=-1;//head不能放在这里i清,否则为0
}
queue<int>Q;
while(!Q.empty())
Q.pop();
book[s]=1;
dist[s]=0;
Q.push(s);
while(!Q.empty())
{
int u=Q.front();
Q.pop();
book[u]=0;
for(int i=head[u]; i!=-1; i=e[i].nextt)
{
int v=e[i].v;
if(e[i].cap>e[i].flow&&dist[v]>dist[u]+e[i].cost)
{
dist[v]=dist[u]+e[i].cost;
pre[v]=i;
if(book[v]==0)
{
book[v]=1;
Q.push(v);
}
}
}
}
if(dist[t]!=inf)
return 1;
return 0;
}
int MCMF()
{
int flow=0,cost=0;
while(SPFA())
{
int minn=inf;
for(int i=pre[t]; i!=-1; i=pre[e[i^1].v])
minn=min(minn,e[i].cap-e[i].flow);
for(int i=pre[t]; i!=-1; i=pre[e[i^1].v])
{
e[i].flow+=minn;
e[i^1].flow-=minn;
cost+=e[i].cost*minn;
}
flow+=minn;
}
return cost;
}
int main()
{
while(~scanf("%d",&n))
{
tot=-1,s=1,t=2*n*n;
memset(head,-1,sizeof(head));
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
scanf("%d",&a[i][j]);
add((i-1)*n+j, (i+n-1)*n+j,1,-a[i][j]);//cap,cost
if(i!=n)
add((i+n-1)*n+j,i*n+j,1,0);
if(j!=n)
add((i+n-1)*n+j,(i-1)*n+j+1,1,0);
}
}
add(s,n*n+1,1,0);//源点到起点,容量2,费用0
add(n*n,t,1,0);//终点到汇点,容量2,费用0
int ans=MCMF();
printf("%d\n",-ans);//最小费用最大流
}
return 0;
}