题目传送门
题意:给出一个$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 }