Luogu2045 方格取数加强版(K取方格数) 费用流

题目传送门

题意:给出一个$N \times N$的方格,每个格子中有一个数字。你可以取$K$次数,每次取数从左上角的方格开始,每一次只能向右或向下走一格,走到右下角结束,沿路的方格中的数字将会被取出。每个方格中的数字只能被取出一次。问取出数字的和的最大值。$N \leq 50 , K \leq 10$,数字大小$\leq 1000$


Anson队爷:你看NOIP也是出过网络流的

俗话说得好,不知道用什么做的时候就用网络流乱搞

考虑费用流,某个格子向其右边与下面的格子连边。如何满足一个点只能取一次的限制?将一个格子拆点成入点和出点,入点向出点之间连一条流为$1$费用为其数字的边,然后连一条流为$K-1$,费用为$0$的边即可。注意(似乎?)会有$0$环,在费用之外额外分一个层会更好。

一个假的费用流Dinic

  1 /*
  2 n*n k取方格数
  3 n <= 50 , k <= min(n,10) 
  4 */
  5 #define MAXN 5015
  6 #include
  7 using namespace std;
  8 
  9 inline int read(){
 10     int a = 0;
 11     char c = getchar();
 12     while(!isdigit(c))
 13         c = getchar();
 14     while(isdigit(c)){
 15         a = (a << 3) + (a << 1) + (c ^ '0');
 16         c = getchar();
 17     }
 18     return a;
 19 }
 20 
 21 struct Edge{
 22     int end , upEd , flow , w;
 23 }Ed[MAXN << 5];
 24 int N , cntEd = 1 , K , ans , head[MAXN] , dis[MAXN] , now[MAXN] , flo[MAXN];
 25 queue < int > q; 
 26 bool inq[MAXN];
 27 
 28 inline void addEd(int a , int b , int c , int d){
 29     Ed[++cntEd].end = b;
 30     Ed[cntEd].upEd = head[a];
 31     Ed[cntEd].flow = c;
 32     Ed[cntEd].w = d;
 33     head[a] = cntEd;
 34 }
 35 
 36 inline bool bfs(){
 37     q.push(1);
 38     memset(dis , -0x3f , sizeof(dis));
 39     memset(flo , 0 , sizeof(flo));
 40     dis[1] = 0;
 41     flo[1] = 1;
 42     while(!q.empty()){
 43         int t = q.front();
 44         q.pop();
 45         inq[t] = 0;
 46         for(int i = head[t] ; i ; i = Ed[i].upEd)
 47             if(Ed[i].flow)
 48                 if(Ed[i].w + dis[t] > dis[Ed[i].end]){
 49                     flo[Ed[i].end] = flo[t] + 1;
 50                     dis[Ed[i].end] = dis[t] + Ed[i].w;
 51                     if(!inq[Ed[i].end]){
 52                         inq[Ed[i].end] = 1;
 53                         q.push(Ed[i].end);
 54                     }
 55                 }
 56     }
 57     memcpy(now , head , sizeof(head));
 58     return dis[N * N << 1] != dis[N * N << 1 | 1];
 59 }
 60 
 61 inline bool dfs(int dir){
 62     if(dir == N * N << 1)
 63         return 1;
 64     for(int& i = now[dir] ; i ; i = Ed[i].upEd)
 65         if(Ed[i].flow && flo[Ed[i].end] == flo[dir] + 1)
 66             if(dis[dir] + Ed[i].w == dis[Ed[i].end])
 67                 if(dfs(Ed[i].end)){
 68                     Ed[i].flow--;
 69                     Ed[i ^ 1].flow++;
 70                     ans += Ed[i].w;
 71                     return 1;
 72             }
 73     return 0;
 74 }
 75 
 76 inline void init(){
 77     N = read();
 78     K = read();
 79     for(int i = 1 ; i <= N ; i++)
 80         for(int j = 1 ; j <= N ; j++){
 81             if(i != N){
 82                 addEd((i - 1 + N) * N + j , i * N + j , K , 0);
 83                 addEd(i * N + j , (i - 1 + N) * N + j , 0 , 0);
 84             }
 85             if(j != N){
 86                 addEd((i - 1 + N) * N + j , (i - 1) * N + j + 1 , K , 0);
 87                 addEd((i - 1) * N + j + 1 , (i - 1 + N) * N + j , 0 , 0);
 88             }
 89         }
 90     for(int i = 1 ; i <= N ; i++)
 91         for(int j = 1 ; j <= N ; j++){
 92             int a = read();
 93             addEd((i - 1) * N + j , (i + N - 1) * N + j , K - 1 , 0);
 94             addEd((i + N - 1) * N + j , (i - 1) * N + j , 0 , 0);
 95             addEd((i - 1) * N + j , (i + N - 1) * N + j , 1 , a);
 96             addEd((i + N - 1) * N + j , (i - 1) * N + j , 0 , -a);
 97         }
 98 }
 99 
100 inline void Dinic(){
101     int d;
102     while(bfs())
103         while(dfs(1));
104 }
105 
106 inline void output(){
107     cout << ans;
108 }
109 
110 int main(){
111     init();
112     Dinic();
113     output();
114     return 0;
115 }

 

转载于:https://www.cnblogs.com/Itst/p/9783989.html

你可能感兴趣的:(Luogu2045 方格取数加强版(K取方格数) 费用流)