欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ5120


题意概括

  原题挺简略的。


题解

  本题好难。

  听了任轩笛大佬<国家队神犇>的讲课才略会。

  然而费用流我也是第一次写。而且这题的费用流是特殊的(简化的)。

  于是我抄了任爷的代码。

  然而,我因为常量写错,找了一个小时……

  这里的work和add我都是直接抄的……懒得打,打完还不一定找得出。反正做法是懂了。

  本题很坑。

  对于40分,还是比较好拿的,插头dp+滚动(然而我忘记开滚动炸了内存……)就可以了。(代码在最后)

  据说插头dp+map可以卡到100?

  这里讲标算做法。

  我们考虑网络流。

  对于两个管口相接,我们只需要建一条流量为1的边。

  但是每一个格子的管子都会转。(除了直线)

  然而旋转需要费用。

  所以我们考虑在自身旋转后,建边要加上费用。

  我们设一个格子的4个接口分别为a,b,c,d,如下图:

  

  如果分情况讨论,那么实在麻烦。实际上我们可以通过自身连边来完成旋转。

  首先我们考虑管子的本质情况:有7种。那么我们可以有相印的连边方案。(二元组中左边为容量,右边为费用)

  

这个……自身建图是不用的。

这个,以及下面的那个建不出图,所以题目说了不能转;哈哈哈哈哈哈哈

  还有一种情况是空的,那么也是不需要自身转移的。

  通过自身转移的建图,我们就可以通过费用来完成旋转。

  然后就是源点的建边,汇点的建边。

  当然,别忘记,对于每一个格子。

  然而,我们要记得连上连接接口的边。

  但是我们发现,这个不仔细考虑会错掉。

  我们把格子按照(行号+列号)的奇偶性黑白染色。

  我们发现黑格子的管子一定是接上白格子的。

  于是我们人为规定,让流从黑格子流向白格子。

  这样就能确定某一个节点(其实就是方格边上的4点)是从源点流来还是流向汇点了。

  最后感叹一句:这样做真妙啊!


代码

  1. #include <cstring>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <algorithm>
  5. #include <cmath>
  6. #include <map>
  7. using namespace std;
  8. typedef long long LL;
  9. const int NM=2005,N=8205,M=N*20,Inf=2333333;
  10. int dx[4]={-1, 0, 1, 0};
  11. int dy[4]={ 0, 1, 0,-1};
  12. struct Edge{
  13. int x,y,z,flow,nxt;
  14. Edge (){}
  15. Edge (int a,int b,int c,int d,int e){
  16. x=a,y=b,z=c,flow=d,nxt=e;
  17. }
  18. }E[M];
  19. int n,m,S,T,cnt,tot,res,ans,sum;
  20. int id[NM][NM][4],dis[N],vis[N],fst[N],Q[N],pre[N];
  21. int cnt_bit_1(int x){
  22. int ans=0;
  23. while (x)
  24. ans+=x&1,x>>=1;
  25. return ans;
  26. }
  27. bool check_block(int x,int y){
  28. return 1<=x&&x<=n&&1<=y&&y<=m;
  29. }
  30. void Add_Edge(int x,int y,int flow,int z){
  31. E[++cnt]=Edge(x,y,z,flow,fst[x]),fst[x]=cnt;
  32. E[++cnt]=Edge(y,x,-z,0,fst[y]),fst[y]=cnt;
  33. }
  34. void Add(int x,int y,int flow,int z,int color){
  35. if (color==1)
  36. Add_Edge(x,y,flow,z);
  37. else if (x==S)
  38. Add_Edge(y,T,flow,z);
  39. else
  40. Add_Edge(y,x,flow,z);
  41. }
  42. void work(int i,int j,int type,int dir,int color){
  43. int *A=id[i][j];
  44. if (type==1){
  45. Add(S,A[dir],1,0,color);
  46. Add(A[dir],A[(dir+1)%4],1,1,color);
  47. Add(A[dir],A[(dir+2)%4],1,2,color);
  48. Add(A[dir],A[(dir+3)%4],1,1,color);
  49. }
  50. if (type==2){
  51. Add(S,A[dir],1,0,color);
  52. Add(S,A[(dir+1)%4],1,0,color);
  53. Add(A[dir],A[(dir+2)%4],1,1,color);
  54. Add(A[(dir+1)%4],A[(dir+3)%4],1,1,color);
  55. }
  56. if (type==3){
  57. Add(S,A[dir],1,0,color);
  58. Add(S,A[(dir+2)%4],1,0,color);
  59. }
  60. if (type==4){
  61. Add(S,A[dir],1,0,color);
  62. Add(S,A[(dir+1)&3],1,0,color);
  63. Add(S,A[(dir+2)&3],1,0,color);
  64. Add(A[dir],A[(dir+3)&3],1,1,color);
  65. Add(A[(dir+1)&3],A[(dir+3)&3],1,2,color);
  66. Add(A[(dir+2)&3],A[(dir+3)&3],1,1,color);
  67. }
  68. if (type==5){
  69. Add(S,A[dir],1,0,color);
  70. Add(S,A[(dir+1)%4],1,0,color);
  71. Add(S,A[(dir+2)%4],1,0,color);
  72. Add(S,A[(dir+3)%4],1,0,color);
  73. }
  74. }
  75. bool SPFA(){
  76. int head=0,tail=0,qmod=8191;
  77. int x,y;
  78. memset(vis,0,sizeof vis);
  79. memset(dis,63,sizeof dis);
  80. memset(pre,0,sizeof pre);
  81. Q[tail=(tail+1)&qmod]=S;
  82. dis[S]=0,vis[S]=1,pre[S]=0;
  83. while (head!=tail){
  84. vis[x=Q[head=(head+1)&qmod]]=0;
  85. for (register int i=fst[x];i;i=E[i].nxt)
  86. if (E[i].flow&&dis[x]+E[i].z<dis[y=E[i].y]){
  87. dis[y]=dis[x]+E[i].z;
  88. pre[y]=i;
  89. if (!vis[y]){
  90. vis[y]=1;
  91. Q[tail=(tail+1)&qmod]=y;
  92. }
  93. }
  94. }
  95. return dis[T]<Inf;
  96. }
  97. void Flowing(){
  98. for (register int i=pre[T];i;i=pre[E[i].x])
  99. E[i].flow--,E[i^1].flow++;
  100. ans++,res+=dis[T];
  101. }
  102. int main(){
  103. scanf("%d%d",&n,&m);
  104. memset(fst,0,sizeof fst);
  105. tot=sum=0,cnt=1;
  106. S=++tot,T=++tot;
  107. for (int i=1;i<=n;i++)
  108. for (int j=1,x;j<=m;j++){
  109. scanf("%d",&x);
  110. for (int k=0;k<4;k++)
  111. id[i][j][k]=++tot;
  112. if (x==1) work(i,j,1,0,(i+j)&1);
  113. if (x==2) work(i,j,1,1,(i+j)&1);
  114. if (x==3) work(i,j,2,0,(i+j)&1);
  115. if (x==4) work(i,j,1,2,(i+j)&1);
  116. if (x==5) work(i,j,3,0,(i+j)&1);
  117. if (x==6) work(i,j,2,1,(i+j)&1);
  118. if (x==7) work(i,j,4,0,(i+j)&1);
  119. if (x==8) work(i,j,1,3,(i+j)&1);
  120. if (x==9) work(i,j,2,3,(i+j)&1);
  121. if (x==10) work(i,j,3,1,(i+j)&1);
  122. if (x==11) work(i,j,4,3,(i+j)&1);
  123. if (x==12) work(i,j,2,2,(i+j)&1);
  124. if (x==13) work(i,j,4,2,(i+j)&1);
  125. if (x==14) work(i,j,4,1,(i+j)&1);
  126. if (x==15) work(i,j,5,0,(i+j)&1);
  127. sum+=cnt_bit_1(x);
  128. }
  129. for (int i=1;i<=n;i++)
  130. for (int j=1;j<=m;j++)
  131. if ((i+j)&1)
  132. for (int k=0;k<4;k++){
  133. int x=i,y=j;
  134. int x_=i+dx[k],y_=j+dy[k];
  135. if (!check_block(x_,y_))
  136. continue;
  137. Add_Edge(id[x][y][k],id[x_][y_][(k+2)%4],1,0);
  138. }
  139. ans=res=0;
  140. while (SPFA())
  141. Flowing();
  142. printf("%d",(ans*2==sum)?res:-1);
  143. return 0;
  144. }

  

40分的插头dp

  1. #include <cstring>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <algorithm>
  5. #include <cmath>
  6. using namespace std;
  7. const int N=2005,M=20,S=1<<17,Inf=100000;
  8. int n,m,b[N],a[N],dp[2][S];
  9. int Ha(int a,int b){
  10. //1<=a<=n,1<=b<=m
  11. return (a-1)*m+b-1 + 1;
  12. }
  13. void Hab(int v,int &a,int &b){
  14. v--;
  15. a=v/m+1;
  16. b=v%m+1;
  17. }
  18. int rotate(int v,int k){
  19. return ((v>>k)|(v<<(4-k)))&15;
  20. }
  21. int main(){
  22. bool tag=0;
  23. scanf("%d%d",&n,&m);
  24. for (int i=1;i<=n;i++)
  25. for (int j=1;j<=m;j++)
  26. scanf("%d",&b[Ha(i,j)]);
  27. if (n<m)
  28. tag=1;
  29. if (tag==1){
  30. swap(n,m);
  31. for (int i=1;i<=n;i++)
  32. for (int j=1;j<=m;j++)
  33. a[Ha(i,j)]=rotate(b[(m-j)*n+i],3);/*b[(m-j)*n+i]*/
  34. // (m-j+1,i)
  35. }
  36. else
  37. for (int i=1;i<=n*m;i++)
  38. a[i]=b[i];
  39. /* for (int i=1;i<=n;i++,puts(""))
  40. for (int j=1;j<=m;j++)
  41. printf("%d ",a[Ha(i,j)]);*/
  42. memset(dp,63,sizeof dp);
  43. dp[0][0]=0;
  44. int s=1<<(m+1),x1=0,x0=1;
  45. for (int i=1;i<=n*m;i++){
  46. x1^=1,x0^=1;
  47. memset(dp[x1],63,sizeof dp[x1]);
  48. int x,y,cost;
  49. Hab(i,x,y);
  50. for (int j=0;j<s;j++){
  51. if (dp[x0][j]>Inf)
  52. continue;
  53. if (a[i]==5){
  54. if ((j&1)||!((j>>y)&1))
  55. continue;
  56. dp[x1][j]=min(dp[x1][j],dp[x0][j]);
  57. continue;
  58. }
  59. if (a[i]==10){
  60. if (y==m)
  61. continue;
  62. if (!(j&1)||((j>>y)&1))
  63. continue;
  64. dp[x1][j]=min(dp[x1][j],dp[x0][j]);
  65. continue;
  66. }
  67. for (int k=0;k<4;k++){
  68. int dir=rotate(a[i],k);
  69. int a1=dir&1,a2=(dir>>1)&1,a3=(dir>>2)&1,a4=(dir>>3)&1;
  70. if ((a1^((j>>y)&1))||(a4^(j&1))||(y==m&&a2))
  71. continue;
  72. int j_=((j&(s-2))|a2)&(s-1-(1<<y))|(a3<<y);
  73. dp[x1][j_]=min(dp[x1][j_],dp[x0][j]+(k==3?1:k));
  74. }
  75. }
  76. }
  77. /* for (int i=0;i<=n*m;i++,puts(""))
  78. for (int j=0;j<s;j++)
  79. printf("%10d ",dp[i][j]);*/
  80. int ans=dp[x1][0];
  81. if (ans>Inf)
  82. ans=-1;
  83. printf("%d",ans);
  84. return 0;
  85. }

  

BZOJ5120 [2017国家集训队测试]无限之环 费用流的更多相关文章

  1. BZOJ 5120: [2017国家集训队测试]无限之环(费用流)

    传送门 解题思路 神仙题.调了一个晚上+半个上午..这道咋看咋都不像图论的题竟然用费用流做,将行+列为奇数的点和偶数的点分开,也就是匹配问题,然后把一个点复制四份,分别代表这个点的上下左右接头,如果有 ...

  2. [BZOJ5120] [2017国家集训队测试]无限之环

    Description 曾经有一款流行的游戏,叫做InfinityLoop,先来简单的介绍一下这个游戏: 游戏在一个n×m的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在方格某些方向的边界的中 ...

  3. bzoj 5120: [2017国家集训队测试]无限之环【最小费用最大流】

    玄妙的建图-- 这种平衡度数的题按套路是先黑白染色然后分别连ST点,相邻格子连黑向白连费用1流量0的边,然后考虑费用怎么表示 把一个点拆成五个,上下左右中,中间点黑白染色连ST, 对于连S的点,中点连 ...

  4. bzoj 5120 [2017国家集训队测试]无限之环——网络流

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5120 旋转的话相当于去掉一个插头.新增一个插头,所以在这两个插头之间连边并带上费用即可. 网 ...

  5. BZOJ.5120.[清华集训2017]无限之环(费用流zkw 黑白染色)

    题目链接 LOJ 洛谷 容易想到最小费用最大流分配度数. 因为水管形态固定,每个点还是要拆成4个点,分别当前格子表示向上右下左方向. 然后能比较容易地得到每种状态向其它状态转移的费用(比如原向上的可以 ...

  6. BZOJ_2622_[2012国家集训队测试]深入虎穴_最短路

    BZOJ_2622_[2012国家集训队测试]深入虎穴_最短路 Description 虎是中国传统文化中一个独特的意象.我们既会把老虎的形象用到喜庆的节日装饰画上,也可能把它视作一种邪恶的可怕的动物 ...

  7. 【BZOJ2622】[2012国家集训队测试]深入虎穴 次短路

    [BZOJ2622][2012国家集训队测试]深入虎穴 Description 虎是中国传统文化中一个独特的意象.我们既会把老虎的形象用到喜庆的节日装饰画上,也可能把它视作一种邪恶的可怕的动物,例如“ ...

  8. 2017国家集训队作业Atcoder题目试做

    2017国家集训队作业Atcoder题目试做 虽然远没有达到这个水平,但是据说Atcoder思维难度大,代码难度小,适合我这种不会打字的选手,所以试着做一做 不知道能做几题啊 在完全自己做出来的题前面 ...

  9. 2017国家集训队作业[agc016b]Color Hats

    2017国家集训队作业[agc016b]Color Hats 题意: 有\(N\)个人,每个人有一顶帽子.帽子有不同的颜色.现在,每个人都告诉你,他看到的所有其它人的帽子共有多少种颜色,问有没有符合所 ...

随机推荐

  1. Unity3d跨平台原理

    知乎的一个提问:unity3d跨平台原理 一些资料: IL IL是.NET框架中中间语言(Intermediate Language)的缩写.使用.NET框架提供的编译器可以直接将源程序编译为.exe ...

  2. 法律AI数据及应用

    本文简单列举了法律AI目前的应用,数据集,研究方向. 历史 1970年,Buchanan和Headrick发表文章"关于人工智能和法律推理的一些猜测",讨论了对法律研究和推理进行建 ...

  3. 第5月第7天 php slim

    1. <?php require 'Slim/Slim.php'; require 'DBManagement.php'; \Slim\Slim::registerAutoloader(); $ ...

  4. POJ 1811 Prime Test (Rabin-Miller强伪素数测试 和Pollard-rho 因数分解)

    题目链接 Description Given a big integer number, you are required to find out whether it's a prime numbe ...

  5. JavaScript学习 - 基础(七) - DOM event(事件)

    DOM event(事件) 定义事件: // 定义事件: //方式一,直接在标签上定义事件 // 方式二: var a11 = document.getElementsByName('a11')[0] ...

  6. 2018-2019-2 网络对抗技术 20165230 Exp5 MSF基础应用

    目录 1.实验内容 2.基础问题回答 3.实验内容 任务一:一个主动攻击实践 漏洞MS08_067(成功) 任务二:一个针对浏览器的攻击 ms11_050(成功) ms14_064(成功) 任务三:一 ...

  7. HTML学习笔记06-连接

    HTML超链接 HTML使用标签<a>来设置文本超链接. 超链接可以是文字,也可以是图片,点击这些内容跳转到新的文档或当前文档的某个部分 代码类似这样: <a href=" ...

  8. 安装installshield问题

    install designer中 general information 选择setup languages shortcuts编辑  开始  中显示目录 文件路径 C:\Program Files ...

  9. python内存数据库pydblite

    Pure-Python engine 最近由于项目开发中发现python informixDB模块对多线程的支持非常不好,当开启两个线程同时连接informix数据库的时候,数据库会报错,显示SQL ...

  10. python风流史

    python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆(中文名字:龟叔)为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言 ...