神奇的建模。。。原题链接

如果你真的把交换看成交换,就\(GG\)了。首先我们要把交换看成是白棋的移动。

然后,很容易的就想到建模的大致思路:建立超级源点S和超级汇点T,从S向初始局面每个白棋所在的格子连边,从目标局面每个白棋所在的格子向T连边,在相邻的格子中间加一些有限制的边,跑一波费用流。

那中间的那些边应该怎么加呢,先考虑把每个格子拆成两个点,中间连一条以交换次数为流量上限的边。但是这样会有问题,考虑某个白棋移动路径上的点,其实只有开头的格子交换了一次,中间的都是两次。只拆两个体现不了在中间或首尾的差别,那我们就拆三个。

把一个格子拆成三个点:TYPE1TYPE2TYPE3,分别表示流入,当前格子,流出。与ST的连边改为向TYPE2连边,与相邻格子的连边在TYPE1TYPE3之间连,另外从TYPE2TYPE1TYPE3连边,这样就能体现在路径上位置的差别了。

那这些边的容量和费用该怎么设置呢?

1.TYPE1TYPE3之间的连边容量为\(INF\),费用为\(1\)。

2.STYPE2TYPE2T容量为\(1\),费用为\(0\)。

3.TYPE2TYPE1TYPE3的连边(重点):

对于某个格子(假设它的交换上限为\(w\),"/"号代表整除):

①若初始为白棋,目标为黑棋,则从TYPE1TYPE2连一条容量为\(w/2\),费用为\(0\)的边,从TYPE2TYPE3连一条容量为\((w+1)/2\),费用为\(0\)的边

②若初始为黑棋,目标为白棋,则从TYPE1TYPE2连一条容量为\((w+1)/2\),费用为\(0\)的边,从TYPE2TYPE3连一条容量为\(w/2\),费用为\(0\)的边

③若始末颜色相同,则从TYPE1TYPE2连一条容量为\(w/2\),费用为\(0\)的边,从TYPE2TYPE3连一条容量为\(w/2\),费用为\(0\)的边

为什么权值要这样设,因为我们要保持收支平衡,使得每个格子在交换之后的颜色是对的。还有我们考虑的是白棋的移动,因此①应该会多一次流出,于是我们尽量把零头分给TYPE2TYPE3之间的那条边。②同理。

注意判\(-1\)!

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define pii pair<int, int>
  4. #define mp make_pair
  5. #define pb push_back
  6. #define N 10000
  7. #define INF 0x3f3f3f3f
  8. int n, m, S, T;
  9. struct MCMF
  10. {
  11. struct Edge
  12. {
  13. int from, to, cap, flow, cost;
  14. };
  15. int S, T;
  16. int d[N+5], a[N+5], vis[N+5], pre[N+5];
  17. vector<int> G[N+5];
  18. vector<Edge> edges;
  19. void init(int S, int T)
  20. {
  21. this->S = S, this->T = T;
  22. }
  23. void addEdge(int from, int to, int cap, int cost)
  24. {
  25. edges.pb(Edge{from, to, cap, 0, cost}), edges.pb(Edge{to, from, 0, 0, -cost});
  26. G[from].pb(edges.size()-2), G[to].pb(edges.size()-1);
  27. }
  28. int SPFA(int &flow, int &cost)
  29. {
  30. memset(d, 0x3f, sizeof d), memset(vis, 0, sizeof vis);
  31. d[S] = 0, a[S] = INF, vis[S] = 1, pre[S] = 0;
  32. queue<int> q;
  33. q.push(S);
  34. while(!q.empty())
  35. {
  36. int u = q.front(); q.pop();
  37. vis[u] = 0;
  38. for(int i = 0; i < G[u].size(); ++i)
  39. {
  40. Edge &e = edges[G[u][i]];
  41. if(e.cap > e.flow && d[e.to] > d[u]+e.cost)
  42. {
  43. d[e.to] = d[u]+e.cost;
  44. pre[e.to] = G[u][i];
  45. a[e.to] = min(a[u], e.cap-e.flow);
  46. if(!vis[e.to]) vis[e.to] = 1, q.push(e.to);
  47. }
  48. }
  49. }
  50. if(d[T] == INF) return 0;
  51. int u = T;
  52. flow += a[T], cost += d[T]*a[T];
  53. while(u != S)
  54. {
  55. edges[pre[u]].flow += a[T], edges[pre[u]^1].flow -= a[T];
  56. u = edges[pre[u]].from;
  57. }
  58. return 1;
  59. }
  60. pii minCost()
  61. {
  62. int flow = 0, cost = 0;
  63. while(SPFA(flow, cost));
  64. return mp(flow, cost);
  65. }
  66. }solver;
  67. int Val(char c)
  68. {
  69. return c-'0';
  70. }
  71. int TYPE1(int i, int j)
  72. {
  73. return ((i-1)*m+j)*3-2;
  74. }
  75. int TYPE2(int i, int j)
  76. {
  77. return ((i-1)*m+j)*3-1;
  78. }
  79. int TYPE3(int i, int j)
  80. {
  81. return ((i-1)*m+j)*3;
  82. }
  83. int a[25][25], b[25][25], w[25][25];
  84. int d[8][2] = {-1,0,1,0,0,-1,0,1,-1,-1,-1,1,1,-1,1,1};
  85. //0:black 1:white
  86. int main()
  87. {
  88. scanf("%d%d", &n, &m);
  89. S = 0, T = 3*n*m+1;
  90. solver.init(S, T);
  91. char c;
  92. int sum = 0;
  93. for(int i = 1; i <= n; ++i)
  94. for(int j = 1; j <= m; ++j) cin >> c, a[i][j] = Val(c), sum += a[i][j];
  95. for(int i = 1; i <= n; ++i)
  96. for(int j = 1; j <= m; ++j) cin >> c, b[i][j] = Val(c);
  97. for(int i = 1; i <= n; ++i)
  98. for(int j = 1; j <= m; ++j) cin >> c, w[i][j] = Val(c);
  99. for(int i = 1; i <= n; ++i)
  100. for(int j = 1; j <= m; ++j)
  101. {
  102. if(a[i][j]) solver.addEdge(S, TYPE2(i, j), 1, 0);
  103. if(b[i][j]) solver.addEdge(TYPE2(i, j), T, 1, 0);
  104. if(a[i][j] == 0 && b[i][j] == 1)
  105. solver.addEdge(TYPE1(i, j), TYPE2(i, j), (w[i][j]+1)/2, 0), solver.addEdge(TYPE2(i, j), TYPE3(i, j), w[i][j]/2, 0);
  106. else if(a[i][j] == 1 && b[i][j] == 0)
  107. solver.addEdge(TYPE1(i, j), TYPE2(i, j), w[i][j]/2, 0), solver.addEdge(TYPE2(i, j), TYPE3(i, j), (w[i][j]+1)/2, 0);
  108. else
  109. solver.addEdge(TYPE1(i, j), TYPE2(i, j), w[i][j]/2, 0), solver.addEdge(TYPE2(i, j), TYPE3(i, j), w[i][j]/2, 0);
  110. for(int k = 0, ti, tj; k < 8; ++k)
  111. {
  112. ti = i+d[k][0], tj = j+d[k][1];
  113. if(ti < 1 || ti > n || tj < 1 || tj > m) continue;
  114. solver.addEdge(TYPE3(i, j), TYPE1(ti, tj), INF, 1);
  115. }
  116. }
  117. pii res = solver.minCost();
  118. if(res.first != sum) printf("-1\n");
  119. else printf("%d\n", res.second);
  120. return 0;
  121. }

洛谷P3159 交换棋子 神奇的网络流的更多相关文章

  1. 【BZOJ1458】【洛谷4311】士兵占领(网络流)

    [BZOJ1458][洛谷4311]士兵占领(网络流) 题面 BZOJ权限题,洛谷真好 Description 有一个M * N的棋盘,有的格子是障碍.现在你要选择一些格子来放置一些士兵,一个格子里最 ...

  2. 洛谷 P2763 试题库问题(网络流24题之一)

    题目描述 «问题描述: 假设一个试题库中有n道试题.每道试题都标明了所属类别.同一道题可能有多个类别属性.现要从题库中抽取m 道题组成试卷.并要求试卷包含指定类型的试题.试设计一个满足要求的组卷算法. ...

  3. 洛谷 P3159(BZOJ 2668)[CQOI2012]交换棋子

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

  4. [bzoj2668] [洛谷P3159] [cqoi2012] 交换棋子

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

  5. 洛谷P3159 [CQOI2012]交换棋子

    巧妙的拆点方式,首先把1看成黑点,0看成空的,几次交换就可以看成一条路径 1)从容量上看,这条路径为1-2-2-2-2-2----2-1 2)从费用上看,这条路径每条边费用都是1 于是用一种巧妙的拆点 ...

  6. 洛谷 [P2756] 飞行员配对方案问题 网络流实现

    网络流实现二分图匹配 对于x集合的每一个点连一条从源点出发的容量为一的边,对于y集合的每一个点连一条到汇点的容量为一的边,跑最大流 #include <iostream> #include ...

  7. 洛谷P3158 放棋子 [CQOI2011] dp+数论

    正解:dp+数论 解题报告: 传送门! 考虑对每种颜色的棋子单独考虑鸭,那显然有,当某一行或某一列已经被占据的时候,那一行/一列就不能再放别的颜色的棋子了,相当于直接把那一行/一列直接消了 显然就能考 ...

  8. 【Luogu】P3159交换棋子(超出我能力范围的费用流)

    题目链接 明显超出我能力范围. 只放题解. 再放代码. #include<cstring> #include<algorithm> #include<cstdio> ...

  9. 洛谷P4009汽车加油行驶问题——网络流24题(最短路)

    题目:https://www.luogu.org/problemnew/show/P4009 网络流24题中不是网络流的最短路题: 把每个点拆成各个油量上的点,根据要求连边即可: 注意:点数最大为10 ...

随机推荐

  1. 关于图片适配不同尺寸的image View(实战)

    分享人:广州华软 佐罗 一. 前言 在前端开发过程中,设计稿中往往只提供一张图片,但是app内需要用到的尺寸各种各样. 同时图片不仅是信息的直接表达,也会为网站起到美观点缀的作用,图片的变形.过分裁切 ...

  2. 【升鲜宝】生鲜配送管理系统_升鲜宝 V2.0 按客户商品分类分开打印配送与按客户商品分类导出相关订单商品相关说明(一)

    [升鲜宝]生鲜配送管理系统_升鲜宝 V2.0 按[客户]的商品分类分开打印(配送单)与按[客户]商品分类[对账单]导出相关销售订单商品功能相关说明(一) 业务场景概述与痛点 1.中小学校食堂的客户,每 ...

  3. 解决laravel Class 'Doctrine\DBAL\Driver\PDOMySql\Driver' not found 错误

    这个错误的原因来自于没有安装一个依赖库: 官方文档说明如下: Modifying Columns Prerequisites Before modifying a column, be sure to ...

  4. linux/shell/bash 自动输入密码或文本

    linux有些命令需要输入密码,比如ssh或su,又不能通过参数指定,正常只能手动输入.这让人多少有些懊恼,尽管这样很安全! 破解:expect 默认没这个东西,需要安装 apt/yum instal ...

  5. Hive分桶

    1.简介 分桶表是对列值取哈希值的方式将不同数据放到不同文件中进行存储.对于hive中每一个表,分区都可以进一步进行分桶.由列的哈希值除以桶的个数来决定数据划分到哪个桶里. 2.适用场景 1.数据抽样 ...

  6. FelxCell常用属性设置(未完待续......)

    this.grid1.AllowUserPaste//返回或设置是否允许用户粘贴文字和格式 grid1.Cell(Rows, 1).WrapText = true;//设置单元格自动换行

  7. Centos7 安装mysql-8.0.13(rpm)

    yum or rpm? yum安装方式很方便,但是下载mysql的时候从官网下载,速度较慢. rpm安装方式可以从国内镜像下载mysql的rpm包,比较快.rpm也适合离线安装. 环境说明 操作系统: ...

  8. 解释型语言VS编译型语言

    前言 计算机不能直接理解除机器语言以外的语言,所以只有把程序员编写的程序翻译成机器语言,计算机才能够执行程序. 将其他语言翻译成机器语言的工具,被称之为:编译器. 编译器的翻译方式有两种:编译和解释. ...

  9. 影响Linux发展的四位天才黑客

    影响Linux发展的四位天才黑客 相信大家对 Linux 再熟悉不过了.我们都知道 Linux继承自 Unix,但其实他们上一代还有一个 Multics.从最早的 Multics 发展到最早版本的 L ...

  10. 毕业设计(3)基于MicroPython的篮球计时计分器模型的设计与实现

    前言 我们身边有不少人都喜欢看篮球比赛或者经常打篮球.说起篮球,肯定要提到NBA(美国职业篮球联赛),现在也正是NBA 18-19赛季常规赛的时候.最近3月5日,韦少22分帮助雷霆终结了四连败,哈登4 ...