POJ3422 Kaka's Matrix Travels

描述

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 theK travels.

输入

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.

输出

The maximum SUM Kaka can obtain after his Kth travel.

样例输入

3 2
1 2 3
0 2 1
1 4 2

样例输出

15

来源

POJ Monthly--2007.10.06, Huang, Jinsong

正解:网络流(最小费用最大流)

解题报告:

      2016年北京大学信息学奥赛训练营上机考核第三场 B题

  大致题意是给定一个网格图,从左上角走到右下角,并且拿走经过的格子里面的数,只能往右或者往下走。总共走k次,问总和最大是多少。

  因为以前做过一道NOIP的类似题目,所以一见到这道题就马上想DP,结果发现不对。NOIP那道题是双线程DP,空间和时间都是允许的,而这道题是k次,要开2*k维,显然会炸。

  机智的我马上想起以前在codevs上面看过这道题,似乎是一模一样的。并且记得是网络流。然而最后没打出来,并不会建模,还是网络流题目做太少了。

  这道题其实很简单,模型也很容易想到。可以看出本题就是求最大费用最大流,我们把图里的数字改成负数就可以变成最小费用最大流,最后取相反数就可以了。

  我参考了这份博客,讲的还比较清楚:http://blog.csdn.net/qq172108805/article/details/7857503

      

  

  如上图所示,我们把一个点拆成两个点,并且用两条边相连,一条权值为这个点的权值,流量为1;另一条权值为0,流量为k-1(反向边都要分别添加)

  再加上点之间的相连,容易想到这样建模可以达到题目要求的目的

  代码如下:

      

  1 //It is made by jump~
  2 #include <iostream>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <cstdio>
  6 #include <cmath>
  7 #include <algorithm>
  8 #include <ctime>
  9 #include <vector>
 10 #include <queue>
 11 #include <map>
 12 #ifdef WIN32   
 13 #define OT "%I64d"
 14 #else
 15 #define OT "%lld"
 16 #endif
 17 using namespace std;
 18 typedef long long LL;
 19 const int MAXN = 60;
 20 const int MAXD = 5011;
 21 const int MAXM = 100011;
 22 const int inf = 2000000000;
 23 int n,k;
 24 int a[MAXN][MAXN];
 25 int first[MAXD],dis[MAXD];
 26 bool pd[MAXD];
 27 int pre[MAXD];
 28 int ecnt=1;
 29 int s,t;
 30 int ans;
 31 queue<int>Q;
 32 
 33 struct edge{
 34     int to,f,next,u;
 35     int w;
 36 }e[MAXM];
 37 
 38 inline int getint()
 39 {
 40     int w=0,q=0;
 41     char c=getchar();
 42     while((c<'0' || c>'9') && c!='-') c=getchar();
 43     if (c=='-')  q=1, c=getchar();
 44     while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
 45     return q ? -w : w;
 46 }
 47 
 48 inline void link(int x,int y,int w,int z){
 49     e[++ecnt].next=first[x]; first[x]=ecnt; e[ecnt].to=y; e[ecnt].f=z; e[ecnt].w=w; e[ecnt].u=x;
 50     e[++ecnt].next=first[y]; first[y]=ecnt; e[ecnt].to=x; e[ecnt].f=0; e[ecnt].w=-w; e[ecnt].u=y;
 51 }
 52 
 53 inline int spfa(){//EK
 54     //根据边权跑最短路
 55     while(!Q.empty()) Q.pop();
 56     memset(pd,0,sizeof(pd)); 
 57     //memset(pre,0,sizeof(pre));
 58     //memset(flow,0,sizeof(flow));
 59     for(int i=0;i<=t;i++) dis[i]=inf,pre[i]=-1;
 60     pd[s]=1; Q.push(s); dis[s]=0; 
 61     while(!Q.empty()){
 62     int u=Q.front(); Q.pop(); pd[u]=0;
 63     //if(u==t) break;
 64     for(int i=first[u];i;i=e[i].next) {
 65         if(e[i].f>0) {
 66         int v=e[i].to;
 67         if(dis[v]>dis[u]+e[i].w) {//限制流
 68             dis[v]=dis[u]+e[i].w;
 69             pre[v]=i;
 70             if(!pd[v]) { Q.push(v); pd[v]=1; }
 71         }
 72         }
 73     }
 74     }
 75     if(dis[t]==inf) return -1;
 76     return 1;
 77 }
 78 
 79 inline int update(){
 80     int total=0; int f=inf;
 81     for(int i=pre[t];e[i].u!=s;i=pre[e[i].u]) f=min(f,e[i].f);
 82 
 83     for(int i=pre[t];e[i].u!=s;i=pre[e[i].u]){
 84     total+=e[i].w*f;
 85     e[i].f-=f;
 86     e[i^1].f+=f;
 87     }
 88 
 89     return total;
 90 }
 91 
 92 inline void solve(){
 93     while(scanf("%d%d",&n,&k)!=EOF) {
 94     for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)  a[i][j]=getint();
 95     ecnt=1;
 96     memset(first,0,sizeof(first));
 97     for(int i=1;i<=n;i++) 
 98         for(int j=1;j<=n;j++) {
 99         int now=(i-1)*n+j-1;
100         link(now*2,now*2+1,-a[i][j],1); link(now*2,now*2+1,0,k-1);//拆成两个点,并且连边
101         }
102 
103     for(int i=1;i<=n;i++) //向右连边
104         for(int j=1;j<n;j++) {//最后一列无需连边
105         int now=(i-1)*n+j-1;
106         link(now*2+1,now*2+2,0,k); 
107         }
108 
109     for(int i=1;i<n;i++) //向下连边
110         for(int j=1;j<=n;j++) {//最后一行无需连边
111         int now=(i-1)*n+j-1;
112         link(now*2+1,(now+n)*2,0,k); 
113         }
114 
115     s=n*n*2; t=s+1;
116     link(s,0,0,k); link(s-1,t,0,k);
117 
118     ans=0;
119     while(1) {
120         int daan=spfa();
121         if(daan==-1) break;
122         ans+=update();
123     }
124     printf("%d
",-ans);
125     }
126 
127 }
128 
129 int main()
130 {
131     solve();
132     return 0;
133 }
原文地址:https://www.cnblogs.com/ljh2000-jump/p/5567551.html