很妙的一道题

对于每个格子,它合法与否,只跟它上下左右的相邻格子有关,所以可以想到黑白染色
(用 (i,j) 表示 i 行 j 列的格子,我把 (i+j) %2 == 0 的格子染成白色,把(i+j)%2 == 1 的格子染成黑色)

关键是怎么描述旋转操作

我朴实的想法是给每个格子建四个点,每个点代表格子可通过旋转达到的一种状态,从格子的初始状态向格子其它状态连边,然后把格子间可以匹配的状态连起来,再然后……就没有然后了

看了题解,真的被惊到了
还是把每个格子拆成4个节点,对应四个方向上的接口
从源点向白格的接口连流量上界1,费用0的边
从黑格的接口向汇点连流量上界1,费用0的边
从黑格的接口向可以匹配的白格的接口连流量上界1,费用0的边
然后旋转操作,就可以通过一种神奇方式描述出来:

 AD  O   BC

把这个看成一个白格(黑格类似,只是连边方向相反)
边(u->v,f,w)代表从 u 到 v,流量上界 f,费用 w 的边
下文描述的旋转方向均为顺时针
1.此格有1个接口:

 A|
D   O   BC

(A->B ,1,1)) 对应转90度
(A->C, 1,2)) 对应转180度
(A->D ,1,1)) 对应转270度
2.此格有2个接口:
情况1:

  A|
D    O —— BC

(A->C, 1,1)) 对应转90度
(B->D ,1,1)) 对应转270度
(A->C, 1,1))+(B->D ,1,1)) 对应转180度
情况2:

 A|
D   O   B|C

不能转,忽略
3.此格有3个接口:

  A|
D    O —— B|C

(A->D ,1,1)) 对应转270度
(B->D, 1,2)) 对应转180度
(C->D ,1,1)) 对应转90度
4.此格有4个接口:

  A|
D —— O —— B|C

转了也没区别,忽略

建完图后跑最小费用最大流即可

如果我表达不清的话,这是样例1的建图,很丑,凑合着看吧

图上标的数代表费用,没标的边默认费用为0,所有边的流量上界默认为1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int inf=0x7fffffff;
const int N=8100;
const int M=200010;
struct Edge{int u,v,f,w,nxt;
}edge[M<<1];
int s,t,head[N],cnt,maxflow,mincost,dis[N],inque[N],pre[N];
queue<int> q;
int n,m,id[2005][2005][4],num,gr[2005][2005],tot;
void add(int u,int v,int f,int w){edge[cnt].u=u;edge[cnt].v=v;edge[cnt].f=f;edge[cnt].w=w;edge[cnt].nxt=head[u];head[u]=cnt++;edge[cnt].u=v;edge[cnt].v=u;edge[cnt].f=0;edge[cnt].w=-w;edge[cnt].nxt=head[v];head[v]=cnt++;
}
bool spfa(){memset(dis,0x7f,sizeof(dis));memset(inque,0,sizeof(inque));memset(pre,-1,sizeof(pre));dis[s]=0;q.push(s);inque[s]=1;while(!q.empty()){int u=q.front();q.pop();inque[u]=0;for(int i=head[u];i!=-1;i=edge[i].nxt){int v=edge[i].v;if(edge[i].f>0&&dis[v]>dis[u]+edge[i].w){dis[v]=dis[u]+edge[i].w;pre[v]=i;if(!inque[v]){q.push(v);inque[v]=1;}}}} if(pre[t]!=-1) return 1;return 0;
}
void EK(){int flow;while(spfa()){flow=inf;int x=pre[t];while(x!=-1){flow=min(edge[x].f,flow);x=pre[edge[x].u];}x=pre[t];while(x!=-1){edge[x].f-=flow;edge[x^1].f+=flow;mincost+=flow*edge[x].w;x=pre[edge[x].u];}maxflow+=flow;}
}
int main(){memset(head,-1,sizeof(head));scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)for(int k=0;k<4;k++)id[i][j][k]=++num;s=++num,t=++num;     for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){scanf("%d",&gr[i][j]);int tt=0,pos[5]={-1,-1,-1,-1,-1},npos=-1;for(int k=0;k<4;k++){if(gr[i][j]&(1<<k)){tot++;pos[++tt]=k;if((i+j)%2==0) add(s,id[i][j][k],1,0);else add(id[i][j][k],t,1,0);}else npos=k;}if(tt==1){if((i+j)%2==0){add(id[i][j][pos[1]],id[i][j][(pos[1]+1)%4],1,1);add(id[i][j][pos[1]],id[i][j][(pos[1]+2)%4],1,2);add(id[i][j][pos[1]],id[i][j][(pos[1]+3)%4],1,1);}else{add(id[i][j][(pos[1]+1)%4],id[i][j][pos[1]],1,1);add(id[i][j][(pos[1]+2)%4],id[i][j][pos[1]],1,2);add(id[i][j][(pos[1]+3)%4],id[i][j][pos[1]],1,1);}}else if(tt==2){if(pos[2]-pos[1]==2) continue;if((i+j)%2==0){add(id[i][j][pos[1]],id[i][j][(pos[1]+2)%4],1,1);add(id[i][j][pos[2]],id[i][j][(pos[2]+2)%4],1,1);}else{add(id[i][j][(pos[1]+2)%4],id[i][j][pos[1]],1,1);add(id[i][j][(pos[2]+2)%4],id[i][j][pos[2]],1,1);}}else if(tt==3){if((i+j)%2==0){add(id[i][j][(npos+1)%4],id[i][j][npos],1,1);add(id[i][j][(npos+2)%4],id[i][j][npos],1,2);add(id[i][j][(npos+3)%4],id[i][j][npos],1,1);}else{add(id[i][j][npos],id[i][j][(npos+1)%4],1,1);add(id[i][j][npos],id[i][j][(npos+2)%4],1,2);add(id[i][j][npos],id[i][j][(npos+3)%4],1,1);}}else continue;}}//一开始这一块的建图错了 int dx[]={-1,0,1,0};int dy[]={0,1,0,-1};for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if((i+j)%2==0){for(int k=0;k<=3;k++){int x=i+dx[k],y=j+dy[k];if(x<1||x>n||y<1||y>m) continue;add(id[i][j][k],id[x][y][(k+2)%4],1,0);}}}}EK();if(tot%2!=0||maxflow!=tot/2) printf("-1\n"); //注意还要判断tot%2!=0的情况 else printf("%d\n",mincost);return 0;
}

[清华集训2017]无限之环(网络流)相关推荐

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

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

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

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

  3. 清华集训2017刷题记录

    2322. 「清华集训 2017」Hello world! 题意 一棵树每个点有点权,每次可以选择两个点\(s, t\),选择步长为\(k\),从\(s\)跳到\(t\)(不足\(k\)步直接到\(t ...

  4. 清华集训2017题解

    ioi赛制好评QAQ 好像啥都记不得了就不写游记了 Day 1 1.1 生成树计数(tree.cpp) 1.1.1. 题意 ​ 有 n≤30000n≤30000 n \le 30000 个点,第 ii ...

  5. uoj#344. 【清华集训2017】我的生命已如风中残烛(计算几何)

    题面 传送门 题解 orzxyx 首先我们发现,一个点如果被到达大于一次,那么这个点肯定在一个环上.所以在不考虑环的情况下每个点只会被到达一次,那么我们就可以直接暴力了 简单来说,我们对每个点\(i\ ...

  6. 【luogu P4005 清华集训2017】小Y和地铁

    题目描述 小 Y 是一个爱好旅行的 OIer.一天,她来到了一个新的城市.由于不熟悉那里的交通系统,她选择了坐地铁. 她发现每条地铁线路可以看成平面上的一条曲线,不同线路的交点处一定会设有 换乘站 . ...

  7. 【清华集训2017】榕树之心

    简要题意:(考虑到某些人搜题解只是懒得看题面) 特殊点从 \(1\) 号点出发,每次选择一个与当前选择连通块相连的点,加入连通块,并且把我们的特殊点向那个选的点移动一格.对每个点求是否能成为我们的特殊 ...

  8. JZOJ 5490. 【清华集训2017模拟11.28】图染色

    Description Input 第一行包括两个整数N,M. 接下来M行每行两个整数u,v,代表存在一条里连接 u,v的无向边.可能存在重边自环. Output 降序输出所有不为0的F(i) .保留 ...

  9. JZOJ 5489. 【清华集训2017模拟11.28】海明距离

    Description 设有一长度为n的初始每个位置均为0的序列A.再给定一个长度为n的01序列B. 有Q个特殊的区间[li,ri],你可以选择将A中li到ri这些位置都变为1,当然你可以选择不变. ...

最新文章

  1. iframe标签快速使用
  2. Linux fs清理文件,linux找出已经删除但磁盘空间未释放的大文件并清空
  3. ios浏览器不支持onblur事件
  4. sketch里的ios控件_30个让你眼前一亮的iOS Swift UI控件!
  5. 在 Kubernetes 集群中使用 MetalLB 作为 Load Balancer(上)
  6. oracle创建索引01652,建立数据表快照导致ora-01652异常
  7. 渐变色彩艺术海报背景素材|感官刺激、个性突出
  8. How can I set ccshared=-fPIC while executing ./configure?
  9. 台式临床化学分析仪行业调研报告 - 市场现状分析与发展前景预测
  10. html中如何设置艺术字体,html里怎么把字体变成艺术字
  11. 微信文章搜索工具, 推荐使用它,简单好用
  12. layim框架+websocket即时通讯功能
  13. 2021C语言全套教程
  14. 还 是 你 太 狠 心
  15. 82.【LibraryManger】
  16. win7 ftp安装搭建,并且上传图片到ftp文件夹下,使用nginx访问下载图片
  17. Bagging (bootstrap aggregating) - 集成方法之一
  18. 杠杆炒股利息一般是多少
  19. 【选品】Shopee虾皮马来西亚和印尼站点分析
  20. 用html+css实现小米官网的模拟

热门文章

  1. sql不等于0怎么表示_数组真的只能从0开始吗?python表示不同意
  2. java中file_详细介绍Java中的File类
  3. leetcode131. 分割回文串
  4. 7-5 流水作业调度 (10 分)(思路+详解+johnson解析)Come Baby!!!!!!!!!!
  5. [JavaWeb-HTML]HTML标签(大部分常用标签介绍)
  6. [PAT乙级]1004 成绩排名
  7. 高等数学上-赵立军-北京大学出版社-题解-练习5.6
  8. 高等数学上-赵立军-北京大学出版社-题解-练习4.5
  9. 如何把自己的经历写成小说_古天乐的经历教会我们:如何在被欺骗以后改善自己的心理状态...
  10. opengl如何画出一个球_少儿美术绘画教程:毛线球