Description
On an N × N chessboard with a non-negative number in each grid, Kaka starts his matrix travels with SUM = 0. For each travel, Kaka moves one rook from the left-upper grid to the right-bottom one, taking care that the rook moves only to the right or down. Kaka adds the number to SUM in each grid the rook visited, and replaces it with zero. It is not difficult to know the maximum SUM Kaka can obtain for his first travel. Now Kaka is wondering what is the maximum SUM he can obtain after his Kth travel. Note the SUM is accumulative during the K travels.
Input
The first line contains two integers N and K (1 ≤ N ≤ 50, 0 ≤ K ≤ 10) described above. The following N lines represents the matrix. You can assume the numbers in the matrix are no more than 1000.
Output
The maximum SUM Kaka can obtain after his Kth travel.
Sample Input
3 2 1 2 3 0 2 1 1 4 2
Sample Output
15
这个题要求的是最大费用最大流,需要拆点,详见注释
#include <iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#define oo 1<<28
#include<queue>
#include<algorithm>
using namespace std;
int pre[210000];
int dis[210000];
int vist[210000];
int head[210000];
int map[10000][10000];
struct node
{
int u;
int v;//与u点相连的点
int f;//容量
int w;//权值
int next;//下一条边
} edge[1100000];
int cnt=0;
int s,t;
void add(int u,int v,int f,int w)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].f=f;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].u=v;
edge[cnt].v=u;
edge[cnt].f=0;
edge[cnt].w=-w;//反向的权值为正向的相反数
edge[cnt].next=head[v];
head[v]=cnt++;
}
void init()
{
memset(pre,-1,sizeof(pre));
memset(vist,0,sizeof(vist));
memset(dis,-1,sizeof(dis));//要求最大值
}
int spfa()
{
int i;
init();
queue<int>q;
dis[s]=0;
vist[s]=1;
q.push(s);
while(!q.empty())//将相邻点进行松弛,直到队列为空
{
int u=q.front();//取出队头
q.pop();
i=head[u];
vist[u]=0;
while(i!=-1)
{
int w=edge[i].w;
int v=edge[i].v;
if(edge[i].f>0&&dis[v]<dis[u]+w)//判断是否可以更新
{
dis[v]=dis[u]+w;//改进s到v点的值
pre[v]=i;//记录此点的前驱
if(!vist[v])//由于距离变小了,如果v点松弛成功且v点不在队列里,因为v点有可能还能改进别的点
{
vist[v]=1;
q.push(v);
}
}
i=edge[i].next;
}
}
if(pre[t]==-1)//如果不在最短路中代表寻找失败
return 0;
return 1;
}
int Cost()
{
int ans=0;
while(spfa())//如果增广路寻找成功
{
int maxx=oo;
int p=pre[t];//初始化P指针
while(p!=-1)
{
maxx=min(edge[p].f,maxx);//求出此增广路上边的最小值
p=pre[edge[p].u];
}
p=pre[t];
while(p!=-1)
{
edge[p].f-=maxx;//正向减去
edge[p^1].f+=maxx;//反向增加
ans+=maxx*edge[p].w;//因为以单位计费,所以应当乘上流量
p=pre[edge[p].u];
}
}
return ans;
}
int main()
{
//嘤嘤嘤。。老是没输出,最后才发现没对head初始化。。真难过,在POJ上还交错了
//这个题我看的题解,要拆点,就是将一个点拆成u,v,自身到自身建边,还有自身到右和下建边
//先由源点到1点,n*n*2到汇点建一条容量为k,权值为0的边,在由自身((i-1)*n+j)*2-1到((i-1)*n+j)*2建两条边
//一条为容量为1,权值为map[i][j],一条为容量为oo,权值为0的边,最后将自身的v点和右和下的u相连,建一条容量为oo,权值为0的边
//注意i,j在边的时候
int n,m,d,k;
int i,j,l;
while(~scanf("%d%d",&n,&k))
{
memset(head,-1,sizeof(head));
int u,v,w;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
scanf("%d",&map[i][j]);
}
}
s=0,t=n*n*2+1;
add(s,1,k,0);//源点与1点相连
add(n*n*2,t,k,0);//n*n*2与汇点相连
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
int a=((i-1)*n+j)*2-1;
int b=((i-1)*n+j)*2;
//printf("%d %d\n",a,b);
add(a,b,1,map[i][j]);//自身出点,入点相连
add(a,b,oo,0);
if(i!=n&&j!=n)
{
add(b,b+1,oo,0);//向右
add(b,b+2*n-1,oo,0);//向下
//printf("%d %d %d\n",b,b+1,b+2*n-1);
}
else if(i!=n&&j==n)
{
add(b,b+2*n-1,oo,0);
}
else if(i==n&&j!=n)
{
add(b,b+1,oo,0);
}
}
}
printf("%d\n",Cost());
}
return 0;
}