Loj #2321. 「清华集训 2017」无限之环

曾经有一款流行的游戏,叫做 *Infinity Loop***,先来简单的介绍一下这个游戏:

游戏在一个 \(n \times m\) 的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在方格某些方向的边界的中点有接口,所有水管的粗细都相同,所以如果两个相邻方格的公共边界的中点都有接头,那么可以看作这两个接头互相连接。水管有以下 \(15\) 种形状:


游戏开始时,棋盘中水管可能存在漏水的地方。

形式化地:如果存在某个接头,没有和其它接头相连接,那么它就是一个漏水的地方。

玩家可以进行一种操作:选定一个含有*非直线型*水管的方格,将其中的水管绕方格中心顺时针或逆时针旋转 \(90\) 度。

直线型水管是指左图里中间一行的两种水管。

现给出一个初始局面,请问最少进行多少次操作可以使棋盘上不存在漏水的地方。

输入格式

第一行两个正整数 \(n,m\),代表网格的大小。

接下来 \(n\) 行每行 \(m\) 个数,每个数是 \([0,15]\) 中的一个,你可以将其看作一个 \(4\) 位的二进制数,从低到高每一位分别代表初始局面中这个格子上、右、

下、左方向上是否有 水管接头。

特别地,如果这个数是 \(0\),则意味着这个位置没有水管。

比如 \(3(0011_{(2)})\) 代表上和右有接头,也就是一个 L 型,而 \(12(1100_{(2)})\) 代表下和左有接头,也就是将 L 型旋转 \(180\) 度。

输出格式

输出共一行,表示最少操作次数。如果无法达成目标,输出 \(-1\).

数据范围与提示

\(n \times m \le 2000\)

orz

好神仙啊!

大致思路就是用一个水管的旋转代替所有水管的旋转。

代码:

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. #define N 10005
  4. using namespace std;
  5. inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
  6. int n,m;
  7. struct road {
  8. int to,next;
  9. int flow,cost;
  10. }s[N*200];
  11. int h[N],cnt=1;
  12. void add(int i,int j,int f,int c) {
  13. s[++cnt]=(road) {j,h[i],f,c};h[i]=cnt;
  14. s[++cnt]=(road) {i,h[j],0,-c};h[j]=cnt;
  15. }
  16. int S,T;
  17. int dis[N];
  18. queue<int>q;
  19. int e[N],fr[N];
  20. bool in[N];
  21. int ans,maxflow;
  22. bool spfa() {
  23. memset(dis,0x3f,sizeof(dis));
  24. dis[S]=0;
  25. q.push(S);
  26. while(!q.empty()) {
  27. int v=q.front();
  28. q.pop();
  29. in[v]=0;
  30. for(int i=h[v];i;i=s[i].next) {
  31. int to=s[i].to;
  32. if(s[i].flow&&dis[to]>dis[v]+s[i].cost) {
  33. dis[to]=dis[v]+s[i].cost;
  34. fr[to]=v;
  35. e[to]=i;
  36. if(!in[to]) in[to]=1,q.push(to);
  37. }
  38. }
  39. }
  40. if(dis[T]>1e9) return 0;
  41. for(int i=T;i!=S;i=fr[i]) {
  42. s[e[i]].flow--;
  43. s[e[i]^1].flow++;
  44. }
  45. maxflow++;
  46. ans+=dis[T];
  47. return 1;
  48. }
  49. vector<int>pipe;
  50. int tot;
  51. int ID[2005][2005][5];
  52. int dx[]={-1,0,1,0},dy[]={0,1,0,-1};
  53. int mp[2005][2005];
  54. int main() {
  55. n=Get(),m=Get();
  56. for(int i=1;i<=n;i++)
  57. for(int j=1;j<=m;j++)
  58. mp[i][j]=Get();
  59. T=n*m+1;
  60. tot=T;
  61. int lpipe=0,rpipe=0;
  62. for(int i=1;i<=n;i++) {
  63. for(int j=1;j<=m;j++) {
  64. int id=(i-1)*m+j;
  65. int a=mp[i][j];
  66. pipe.clear();
  67. for(int k=0;k<4;k++) if(a>>k&1) pipe.push_back(k);
  68. for(int k=0;k<4;k++) ID[i][j][k]=++tot;
  69. if(i+j&1) {
  70. lpipe+=pipe.size();
  71. add(S,id,pipe.size(),0);
  72. for(int k=0;k<pipe.size();k++) add(id,ID[i][j][pipe[k]],1,0);
  73. if(a==10||a==5||a==15) continue ;
  74. if(pipe.size()==1) {
  75. int now=pipe[0];
  76. add(ID[i][j][now],ID[i][j][(now+1)%4],1,1);
  77. add(ID[i][j][now],ID[i][j][(now+2)%4],1,2);
  78. add(ID[i][j][now],ID[i][j][(now+3)%4],1,1);
  79. } else if(pipe.size()==2) {
  80. for(int k=0;k<pipe.size();k++) {
  81. int now=pipe[k];
  82. add(ID[i][j][now],ID[i][j][(now+2)%4],1,1);
  83. }
  84. } else if(pipe.size()==3) {
  85. int now;
  86. for(int k=0;k<4;k++) if(!(a>>k&1)) now=k;
  87. for(int k=0;k<3;k++) {
  88. if((pipe[k]+2)%4==now) add(ID[i][j][pipe[k]],ID[i][j][now],1,2);
  89. else add(ID[i][j][pipe[k]],ID[i][j][now],1,1);
  90. }
  91. }
  92. } else {
  93. rpipe+=pipe.size();
  94. add(id,T,pipe.size(),0);
  95. for(int k=0;k<pipe.size();k++) add(ID[i][j][pipe[k]],id,1,0);
  96. if(a==10||a==5||a==15) continue ;
  97. if(pipe.size()==1) {
  98. int now=pipe[0];
  99. add(ID[i][j][(now+1)%4],ID[i][j][now],1,1);
  100. add(ID[i][j][(now+2)%4],ID[i][j][now],1,2);
  101. add(ID[i][j][(now+3)%4],ID[i][j][now],1,1);
  102. } else if(pipe.size()==2) {
  103. for(int k=0;k<pipe.size();k++) {
  104. int now=pipe[k];
  105. add(ID[i][j][(now+2)%4],ID[i][j][now],1,1);
  106. }
  107. } else if(pipe.size()==3) {
  108. int now;
  109. for(int k=0;k<4;k++) if(!(a>>k&1)) now=k;
  110. for(int k=0;k<3;k++) {
  111. if((pipe[k]+2)%4==now) add(ID[i][j][now],ID[i][j][pipe[k]],1,2);
  112. else add(ID[i][j][now],ID[i][j][pipe[k]],1,1);
  113. }
  114. }
  115. }
  116. }
  117. }
  118. for(int i=1;i<=n;i++) {
  119. for(int j=1;j<=m;j++) {
  120. if(i+j&1) {
  121. for(int d=0;d<4;d++) {
  122. int X=i+dx[d],Y=j+dy[d];
  123. if(1<=X&&X<=n&&1<=Y&&Y<=m) {
  124. add(ID[i][j][d],ID[X][Y][(d+2)%4],1,0);
  125. }
  126. }
  127. }
  128. }
  129. }
  130. if(lpipe!=rpipe) cout<<-1;
  131. else {
  132. while(spfa());
  133. if(maxflow!=lpipe) cout<<-1;
  134. else cout<<ans;
  135. }
  136. return 0;
  137. }

Loj #2321. 「清华集训 2017」无限之环的更多相关文章

  1. UOJ #2321. 「清华集训 2017」无限之环

    首先裂点表示四个方向 一条边上都有插头或者都不有插头,相当于满足流量平衡 最大流 = 插头个数*2时有解 然后求最小费用最大流 黑白染色分别连原点汇点

  2. LOJ2321. 「清华集训 2017」无限之环【费用流】

    LINK 很好的一道网络里题 首先想插头DP的还是出门左转10分代码吧 然后考虑怎么网络流 首先要保证没有漏水 也就是说每个接口一定要有对应的接口 那么发现每个点只有可能和上下左右四个点产生联通关系 ...

  3. [LOJ#2330]「清华集训 2017」榕树之心

    [LOJ#2330]「清华集训 2017」榕树之心 试题描述 深秋.冷风吹散了最后一丝夏日的暑气,也吹落了榕树脚下灌木丛的叶子.相识数年的Evan和Lyra再次回到了小时候见面的茂盛榕树之下.小溪依旧 ...

  4. [LOJ#2329]「清华集训 2017」我的生命已如风中残烛

    [LOJ#2329]「清华集训 2017」我的生命已如风中残烛 试题描述 九条可怜是一个贪玩的女孩子. 这天她在一堵墙钉了 \(n\) 个钉子,第 \(i\) 个钉子的坐标是 \((x_i,y_i)\ ...

  5. Loj #2331. 「清华集训 2017」某位歌姬的故事

    Loj #2331. 「清华集训 2017」某位歌姬的故事 IA 是一名会唱歌的女孩子. IOI2018 就要来了,IA 决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符, ...

  6. Loj #2324. 「清华集训 2017」小 Y 和二叉树

    Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...

  7. Loj 2320.「清华集训 2017」生成树计数

    Loj 2320.「清华集训 2017」生成树计数 题目描述 在一个 \(s\) 个点的图中,存在 \(s-n\) 条边,使图中形成了 \(n\) 个连通块,第 \(i\) 个连通块中有 \(a_i\ ...

  8. [LOJ#2328]「清华集训 2017」避难所

    [LOJ#2328]「清华集训 2017」避难所 试题描述 "B君啊,你当年的伙伴都不在北京了,为什么你还在北京呢?" "大概是因为出了一些事故吧,否则这道题就不叫避难所 ...

  9. [LOJ#2327]「清华集训 2017」福若格斯

    [LOJ#2327]「清华集训 2017」福若格斯 试题描述 小d是4xx9小游戏高手. 有一天,小d发现了一个很经典的小游戏:跳青蛙. 游戏在一个 \(5\) 个格子的棋盘上进行.在游戏的一开始,最 ...

随机推荐

  1. scrapy爬虫学习系列四:portia的学习入门

    系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备:      http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...

  2. Google SwipeRefreshLayout(Goolge官方下拉刷新控件)尝鲜

    前天Google官方终于出了Android刷新控件——SwipeRefreshLayout. 使用前先需要将android.support.v4.jar升级到19.1.升级后,可能会出现SDK版本与A ...

  3. 记一次vue长列表的内存性能分析和优化

    好久没写东西,博客又长草了,这段时间身心放松了好久,都没什么主题可以写了 上周接到一个需求,优化vue的一个长列表页面,忙活了很久也到尾声了,内存使用和卡顿都做了一点点优化,还算有点收获 写的有点啰嗦 ...

  4. vim编辑器详解(week1_day3)--技术流ken

    vi编辑器 作用:编辑文本文件中的内容的工具 命令历史 末行模式中,以:和/开头的命令都有历史纪录,可以首先键入:或/然后按上下箭头来选择某个历史命令. 启动vim 在命令行窗口中输入以下命令即可 v ...

  5. XML——对XML文档的创建与增删改查

    一.创建的第一种方式  //1.创建一个XML文档 XmlDocument doc = new XmlDocument(); //2.创建第一行描述信息 XmlDeclaration dec = do ...

  6. Camera测试之Color & Lens shading Test

    测试目的:测试摄像头成像的均匀性 测试主要设备:均匀光源(DNP灯箱),色温照度计 测试环境:将灯箱光源调至600±100lux 测试注意事项:保证均匀的光源 测试原理: 造成摄像头成像不均匀的原因有 ...

  7. ADO.NET基础学习 二(Command对象)

    ②command对象用来操作数据库.(三个重要的方法:ExecuteNonQuery(),ExecuteReader(),ExecuteScalar()) ⑴以update(改数据)为例,用到Exec ...

  8. mysql 随机数 rand使用

    生成随机数 生成0-3的随机数 SELECT RAND() * 最大不会超过3, SELECT FLOOR(RAND() * ) 上面生成整数的值是0,1,2,3生成的随机整数是1,2,3的话,语句如 ...

  9. Java开发笔记(五十八)简单接口及其实现

    前面介绍了抽象方法及抽象类的用法,看似解决了不确定行为的方法定义,既然叫唤动作允许声明为抽象方法,那么飞翔.游泳也能声明为抽象方法,并且鸡类涵盖的物种不够多,最好把这些行为动作扩展到鸟类这个群体,于是 ...

  10. Android开发——使用intent传递对象

    intent传递对象有两种方法: 方式一:Serializable 方式 方式二:Parcelable方式 在这里不多介绍了,这一篇就是快速上手使用教程,至于详细原理介绍的,请看这一篇http://w ...