题意:你有一个棋盘,某些格子是限制条件,形如"从这里开始下面所有连续空格的和为a"或"从这里开始向右的所有连续空格之和为b"一个格子可以同时拥有两个限制条件。
每个数都必须是正整数。
现在你可以把某些格子加/减1,并花费相应的代价。可以操作无数次。求把棋盘变得合法的最小代价。
解:没想出来,看了题解......
一开始想了数字和条件构成二分图,又想了行列连边,但是始终建不出图来。
行列连边。
因为既可以加又可以减不好搞,我们可以先全部转换成满足条件的最小值,然后往上加。
从最小值到初始值的时候,边权为负。之后边权为正。
不可修改的地方边权为INF。
然后跑一个最小费用可行流就是答案。
如何判断无解?
考虑每条边,如果有的边费用为INF但是有流量,就不合法。
1 #include <cstdio> 2 #include <algorithm> 3 #include <queue> 4 #include <cstring> 5 6 typedef long long LL; 7 const LL N = 2000, M = 1000010, INF = 0x3f3f3f3f, J = 35; 8 9 struct Edge { 10 LL nex, v, c, len; 11 }edge[N << 1]; LL top = 1; 12 13 LL e[N], d[N], vis[N], pre[N], flow[N]; 14 std::queue<LL> Q; 15 LL fr[J][J], val[J][J], v2[J][J], cst[J][J], c2[J][J], m; 16 17 inline void add(LL x, LL y, LL z, LL w) { 18 top++; 19 edge[top].v = y; 20 edge[top].c = z; 21 edge[top].len = w; 22 edge[top].nex = e[x]; 23 e[x] = top; 24 25 top++; 26 edge[top].v = x; 27 edge[top].c = 0; 28 edge[top].len = -w; 29 edge[top].nex = e[y]; 30 e[y] = top; 31 return; 32 } 33 34 inline bool SPFA(LL s, LL t) { 35 memset(d, 0x3f, sizeof(d)); 36 d[s] = 0; 37 flow[s] = INF; 38 vis[s] = 1; 39 Q.push(s); 40 while(!Q.empty()) { 41 LL x = Q.front(); 42 Q.pop(); 43 vis[x] = 0; 44 for(LL i = e[x]; i; i = edge[i].nex) { 45 LL y = edge[i].v; 46 if(edge[i].c && d[y] > d[x] + edge[i].len) { 47 d[y] = d[x] + edge[i].len; 48 pre[y] = i; 49 flow[y] = std::min(flow[x], edge[i].c); 50 if(!vis[y]) { 51 vis[y] = 1; 52 Q.push(y); 53 } 54 } 55 } 56 } 57 return d[t] < INF; 58 } 59 60 inline void update(LL s, LL t) { 61 LL temp = flow[t]; 62 while(t != s) { 63 LL i = pre[t]; 64 edge[i].c -= temp; 65 edge[i ^ 1].c += temp; 66 t = edge[i ^ 1].v; 67 } 68 return; 69 } 70 71 inline LL solve(LL s, LL t, LL &cost) { 72 LL ans = 0; 73 cost = 0; 74 while(SPFA(s, t)) { 75 if(d[t] > 0) { 76 break; 77 } 78 ans += flow[t]; 79 cost += flow[t] * d[t]; 80 update(s, t); 81 } 82 return ans; 83 } 84 85 inline LL id(LL x, LL y) { 86 return (x - 1) * m + y; 87 } 88 89 int main() { 90 LL n, use = 0; 91 scanf("%lld%lld", &n, &m); 92 for(LL i = 1; i <= n; i++) { 93 for(LL j = 1; j <= m; j++) { 94 scanf("%lld", &fr[i][j]); 95 } 96 } 97 for(LL i = 1; i <= n; i++) { 98 for(LL j = 1; j <= m; j++) { 99 if(fr[i][j] == 0) { 100 continue; 101 } 102 else if(fr[i][j] == 1) { 103 scanf("%lld", &val[i][j]); 104 } 105 else if(fr[i][j] == 2) { 106 scanf("%lld", &v2[i][j]); 107 } 108 else if(fr[i][j] == 3) { 109 scanf("%lld%lld", &val[i][j], &v2[i][j]); 110 } 111 else { 112 scanf("%lld", &val[i][j]); 113 } 114 } 115 } 116 for(LL i = 1; i <= n; i++) { 117 for(LL j = 1; j <= n; j++) { 118 if(fr[i][j] == 0) { 119 continue; 120 } 121 else if(fr[i][j] == 1) { 122 scanf("%lld", &cst[i][j]); 123 } 124 else if(fr[i][j] == 2) { 125 scanf("%lld", &c2[i][j]); 126 } 127 else if(fr[i][j] == 3) { 128 scanf("%lld%lld", &cst[i][j], &c2[i][j]); 129 } 130 else { 131 scanf("%lld", &cst[i][j]); 132 } 133 if(cst[i][j] == -1) { 134 cst[i][j] = INF; 135 } 136 if(c2[i][j] == -1) { 137 c2[i][j] = INF; 138 } 139 } 140 } 141 // read over 142 LL lm = n * m; 143 LL s = lm * 2 + 1; 144 LL t = s + 1; 145 for(LL i = 1; i <= n; i++) { 146 for(LL j = 1; j <= m; j++) { 147 if(fr[i][j] == 0) { 148 continue; 149 } 150 if(fr[i][j] == 1 || fr[i][j] == 3) { 151 // id(i, j) 152 LL cnt = 0; 153 for(LL k = i + 1; k <= n; k++) { 154 if(fr[k][j] == 4) { 155 cnt++; 156 } 157 else { 158 break; 159 } 160 } 161 // val[i][j] - cnt 162 if(val[i][j] > cnt) { 163 add(s, id(i, j), val[i][j] - cnt, -cst[i][j]); 164 } 165 add(s, id(i, j), INF, cst[i][j]); 166 use += cst[i][j] * abs(val[i][j] - cnt); 167 } 168 if(fr[i][j] == 2 || fr[i][j] == 3) { 169 LL cnt = 0; 170 for(LL k = j + 1; k <= m; k++) { 171 if(fr[i][k] == 4) { 172 cnt++; 173 } 174 else { 175 break; 176 } 177 } 178 if(v2[i][j] > cnt) { 179 add(id(i, j) + lm, t, v2[i][j] - cnt, -c2[i][j]); 180 } 181 add(id(i, j) + lm, t, INF, c2[i][j]); 182 use += c2[i][j] * abs(v2[i][j] - cnt); 183 } 184 if(fr[i][j] == 4) { 185 LL a, b; 186 for(LL k = i - 1; k >= 1; k--) { 187 if(fr[k][j] == 1 || fr[k][j] == 3) { 188 a = id(k, j); 189 break; 190 } 191 } 192 for(LL k = j - 1; k >= 1; k--) { 193 if(fr[i][k] == 2 || fr[i][k] == 3) { 194 b = id(i, k) + lm; 195 break; 196 } 197 } 198 if(val[i][j] > 1) { 199 add(a, b, val[i][j] - 1, -cst[i][j]); 200 } 201 add(a, b, INF, cst[i][j]); 202 use += cst[i][j] * abs(val[i][j] - 1); 203 } 204 } 205 } 206 207 LL ans; 208 solve(s, t, ans); 209 ans += use; 210 211 /*if(ans >= INF) { 212 puts("-1"); 213 return 0; 214 }*/ 215 for(int i = 2; i <= top; i += 2) { 216 if(abs(edge[i].len) == INF && (edge[i].c && edge[i ^ 1].c)) { 217 puts("-1"); 218 return 0; 219 } 220 } 221 222 printf("%lld", ans); 223 return 0; 224 }
思考:能否不转换为最小?好像无法判断是否合法...