题意:从左上到右下再回到左上,每个点除左上右下只可以走一次,问最大值,
思路:拆点+费用流即可,左上和右下容量为2就行,事实证明...用数组模拟队列容易出事。
# include
# include
# include
# include
# include
using namespace std;
const int INF= 0x3f3f3f3f;
const int maxn = 720010;
int n, cnt=0, Next[maxn], dis[maxn], vis[maxn], pre[maxn];
int a[603][603], source, sink;;
struct node
{
int u, v, w, c, next;
node(){}
node(int u, int v, int w, int c, int next):u(u),v(v),w(w),c(c),next(next){}
}edge[3501000];
inline void scan(int &ret)
{
char c; ret=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
}
inline void out(int x)
{
if(x>9) out(x/10);
putchar(x%10+'0');
}
void add(int u, int v, int w, int c)
{
edge[cnt] = node(u,v,w,c,Next[u]);
Next[u] = cnt++;
edge[cnt] = node(v,u,0,-c,Next[v]);
Next[v] = cnt++;
}
bool spfa()
{
memset(vis, 0, sizeof(vis));
memset(dis, INF, sizeof(dis));
int l=0, r=0;
dis[source] = 0;
vis[source] = 1;
queueq;
q.push(source);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u] = 0;
for(int i=Next[u]; i!=-1; i=edge[i].next)
{
int v=edge[i].v, w=edge[i].w, c=edge[i].c;
if(w>0 && dis[v] > dis[u]+c)
{
dis[v] = dis[u] + c;
pre[v] = i;
if(!vis[v])
{
q.push(v);
vis[v] = 1;
}
}
}
}
return dis[sink] != INF;
}
int full = 0;
int mcmf()
{
int cost = 0;
while(spfa())
{
int imin = INF;
for(int i=sink; i!=source; i=edge[pre[i]].u) imin = min(imin, edge[pre[i]].w);
for(int i=sink; i!=source; i=edge[pre[i]].u)
{
edge[pre[i]].w -= imin;
edge[pre[i]^1].w += imin;
}
cost += imin*dis[sink];
full += imin;
}
return cost;
}
int main()
{
while(~scanf("%d",&n))
{
memset(Next, -1, sizeof(Next));
cnt = 0;
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
{
scan(a[i][j]);
add((i-1)*n+j, (i+n-1)*n+j, 1, -a[i][j]);
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(1, n*n+1, 1, 0);
add((n-1)*n+n, (2*n-1)*n+n, 1, 0);
source = 1, sink = (2*n-1)*n+n;
out(-mcmf());
puts("");
}
return 0;
}