[关闭]
@ljt12138 2017-03-14T14:55:26.000000Z 字数 2428 阅读 816

[CQOI2012]交换棋子

解题报告


题目描述

有一个列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与次交换。

输入输出格式

输入格式:

第一行包含两个整数。以下行为初始状态,每行为一个包含个字符的串,其中表示黑色棋子,表示白色棋子。以下行为目标状态,格式同初始状态。以下n行每行为一个包含数字的字符串,表示每个格子参与交换的次数上限。

输出格式:

输出仅一行,为最小交换总次数。如果无解,输出

解法:

一眼费用流系列...然而这题的处理有点诡异,首先要拆点将点的限制转化为边的限制,的权值是多少?分三种情况讨论:

  1. 既不是起始也不是结束状态:
  2. 既是起始也是结束状态:
  3. 是起始或结束状态:
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 2005;
  4. struct node {
  5. int to, next, f, c, neg;
  6. } edge[MAXN*40];
  7. int head[MAXN], top = 0;
  8. void push(int i, int j, int k, int l)
  9. {
  10. if (i*j == 0) return;
  11. ++top, edge[top] = (node) {j, head[i], k, l, top+1}, head[i] = top;
  12. ++top, edge[top] = (node) {i, head[j], 0, -l, top-1}, head[j] = top;
  13. }
  14. int vis[MAXN], dis[MAXN], S = 2001, T = 2002;
  15. queue<int> que;
  16. int pre[MAXN], pre_edge[MAXN];
  17. const int INF = 233333333;
  18. bool spfa()
  19. {
  20. memset(dis, 127/3, sizeof dis);
  21. memset(pre, 0, sizeof pre);
  22. vis[S] = 1, que.push(S), dis[S] = 0;
  23. while (!que.empty()) {
  24. int tp = que.front(); que.pop(), vis[tp] = 0;
  25. for (int i = head[tp]; i; i = edge[i].next) {
  26. if (edge[i].f == 0 || dis[edge[i].to] <= dis[tp] + edge[i].c) continue;
  27. int to = edge[i].to, c = edge[i].c;
  28. dis[to] = dis[tp] + c, pre[to] = tp, pre_edge[to] = i;
  29. if (!vis[to])
  30. vis[to] = 1, que.push(to);
  31. }
  32. }
  33. return dis[T] < INF;
  34. }
  35. int sap(int &cost)
  36. {
  37. int ans = INF;
  38. for (int i = T; i != S; i = pre[i]) ans = min(ans, edge[pre_edge[i]].f);
  39. for (int i = T; i != S; i = pre[i]) edge[pre_edge[i]].f -= ans, edge[edge[pre_edge[i]].neg].f += ans;
  40. cost += ans*dis[T];
  41. return ans;
  42. }
  43. int mst(int &cost)
  44. {
  45. cost = 0;
  46. int ans = 0;
  47. while (spfa()) ans += sap(cost);
  48. return ans;
  49. }
  50. int n, m;
  51. int A[30][30], B[30][30], C[30][30];
  52. char str[30];
  53. int readln(int A[30][30])
  54. {
  55. int cnt = 0;
  56. for (int i = 1; i <= n; i++) {
  57. scanf("%s", str+1);
  58. for (int j = 1; j <= m; j++)
  59. A[i][j] = str[j]-'0', cnt += A[i][j];
  60. }
  61. return cnt;
  62. }
  63. inline int number(int i, int j, int id)
  64. { return (i<=0||i>n||j<=0||j>m)?0:id*n*m+(i-1)*m+j; }
  65. int dx[] = {1,0,-1,0, 1, 1, -1, -1}, dy[] = {0,1,0,-1, -1, 1, -1, 1};
  66. int main()
  67. {
  68. scanf("%d%d", &n, &m);
  69. int a = readln(A);
  70. int b = readln(B); readln(C);
  71. if (a != b) {puts("-1"); return 0;}
  72. for (int i = 1; i <= n; i++)
  73. for (int j = 1; j <= m; j++) {
  74. if (A[i][j] == 1) push(S, number(i, j, 0), 1, 0);
  75. if (B[i][j] == 1) push(number(i, j, 1), T, 1, 0);
  76. if (A[i][j] == 1 && B[i][j] == 1) push(number(i, j, 0), number(i, j, 1), (C[i][j]+2)/2, 1);
  77. else if (A[i][j] == 1 || B[i][j] == 1) push(number(i, j, 0), number(i, j, 1), (C[i][j]+1)/2, 1);
  78. else push(number(i, j, 0), number(i, j, 1), C[i][j]/2, 1);
  79. for (int k = 0; k < 8; k++) push(number(i, j, 1), number(i+dx[k], j+dy[k], 0), INF, 0);
  80. }
  81. int ans = 0, cost = 0;
  82. ans = mst(cost);
  83. if (ans == a) {
  84. cout << cost-a << endl;
  85. } else
  86. puts("-1");
  87. return 0;
  88. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注