理解不够透彻。好题不可浪费,写题解以增进理解。会陆续补充题目。(咕咕咕)

G Beihang Couple Pairing Comunity 2017

题目链接

Beihang Couple Pairing Comunity 2017

解题思路

第一步:分析题目

首先,如果只判断是否有Poor single dog,这是一道DFS入门题。于是DFS搜一遍,把每个点与出口的距离(DFS距离)表示出来并存储。

一对人拆开没有任何优化,所以看成一个人即可。

这里用到几个变换:

\(state[i][j]\),表示\((i,j)\)这个点上面是墙,是出口,还是人;

\(e[i]\),表示第几个出口的坐标;\(cnt1\),记录有几个出口;\(num[i][j]\),记录这个\((i,j)\)对应的\(e[i]\)中\(i\)的值;

\(p[i]\),表示第几对人的坐标;\(cnt2\),记录有几对人;\(num[i][j]\),记录这个\((i,j)\)对应的\(p[i]\)中\(i\)的值。

第二步:模型建立

\(1.\)很明显,这个题可以建图用网络流求解。(事实证明可以用二分图做而且更简单)

原因有三:

一是数据范围小,连边可行

二是点与点、出口与点之间存在某种制约关系,而这种制约关系能够通过图很好地体现

三是这种制约关系可以通过建图体现,而通过网络流求解。

\(2.\)可以想到,可以二分答案。

原因有二:

一是答案具有单调性,因为时间越长越有可能让更多对人逃离,而时间越短越是正解。如果不明白(都会网络流了还不会二分答案似乎不太可能)请点击这里

二是如果用网络流求解,只能判断某个时间的可行性,故(我认为)不可直接解出。(如果有直接一步网络流的解法请务必联系我)

焦点在于,如何建图。

首先很容易想到的是将源点\(S\)和每一个点\((.)\)连边,每一个点和(与他距离不超过\(mid\)的)(\(mid\)是二分答案的那个\(mid\))出口\((E)\)连边,所有出口和汇点\(T\)连边。

这样连边之后,跑网络流,能够得到WA。

这是为什么呢?

原因在于,只考虑了连边的可行性,而忽视了连边需要考虑的另一个因素:从这个点跑到这个出口的最小时间

也就是说,需要把时间纳入连边的考虑内。

如何纳入考虑?

很自然地想到了拆点。每一个出口对应一个出去的时间,在每一秒里只可以通过一对人,所以可以把一个出口拆成\(mid\)个出口,其中第\(i\)个出口\(E_i\)表示这个出口在时间\(i\)的时候的状态。

于是,综上所述,源S和每一个点\((.)\)连边,每一个点和从\(E_{matrix[i][j]}\)到\(E_{mid}\)的\(E\)连边,每一个拆出来的出口和\(T\)连边。

第三步:写出伪代码

深搜:(比较简单直接码)

  1. void dfsearch(int fir,int x,int y,int dep){
  2. int i;
  3. if(x<0||y<0||x>=n||y>=m)return;
  4. if(vis[x][y]<=dep)return;
  5. vis[x][y]=dep;
  6. if(!state[x][y])return;//墙
  7. if(state[x][y]==1){
  8. if(dep<matrix[fir][number[x][y]])matrix[fir][number[x][y]]=dep;
  9. else return;//必然无法增广更优路线
  10. }
  11. dfsearch(fir,x+1,y,dep+1);
  12. dfsearch(fir,x-1,y,dep+1);
  13. dfsearch(fir,x,y-1,dep+1);
  14. dfsearch(fir,x,y+1,dep+1);
  15. }

Main:

  1. int main(){
  2. scanf("%d",&T);
  3. while(T--){
  4. //initialization
  5. scanf("%d%d",&n,&m);
  6. for(i=0;i<n;i++){
  7. scanf("%s",a);
  8. for(j=0;j<m;j++){
  9. if(a[j]=='X')state[i][j]=0;
  10. else if(a[j]=='E')state[i][j]=1,e[cnt1].x=i,e[cnt1].y=j,number[i][j]=cnt1,cnt1++;
  11. else state[i][j]=2,p[cnt2].x=i,p[cnt2].y=j,number[i][j]=cnt2,cnt2++;
  12. }
  13. }
  14. //DFS:先判断有没有出不去的,同时fill matrix
  15. for(i=0;i<cnt2;i++){
  16. memset(vis,inf,sizeof(vis));
  17. dfsearch(i,p[i].x,p[i].y,0);
  18. int flag=0;
  19. for(j=0;j<cnt1;j++)if(matrix[i][j]<inf){
  20. flag=1;break;
  21. }
  22. if(!flag){
  23. //输出;
  24. //继续下一轮读入
  25. }
  26. }
  27. //再二分答案,判断mid可行不可行
  28. int left=1,right=cnt2;
  29. while(left<right){
  30. int mid=left+right>>1;
  31. //跑最大流=点数,可行
  32. //建图
  33. //跑最大流,如果没有全覆盖(最大流小于点数),则left=mid+1,否则right=mid
  34. }
  35. printf("%d\n",left);
  36. }
  37. return 0;
  38. }

这就结束了。具体细节比如点的下标如何分配不再赘述。

\(Dinic\)别忘加当前弧优化,否则会\(T\)。

AC代码

  1. #include<stdio.h>
  2. #include<string.h>
  3. int state[23][23],n,m;//2人0墙1出口
  4. int number[23][23];
  5. int matrix[402][402];//i:cnt2人,j:cnt1出口
  6. int inf=100000000;
  7. struct Position{
  8. int x,y;
  9. }p[402],e[402];
  10. int cnt1,cnt2;
  11. int s=1,t=2;
  12. int head[320005],cnt=2;
  13. int queue[320005],depth[320005];
  14. int cur[320005];
  15. struct Edge{
  16. int end,len,near;
  17. }edge[32000010];
  18. void add(int begin,int end,int len){
  19. edge[cnt].end=end;
  20. edge[cnt].len=len;
  21. edge[cnt].near=head[begin];
  22. head[begin]=cur[begin]=cnt;
  23. cnt++;
  24. }
  25. int dfs(int,int);
  26. int bfs();
  27. int dinic();
  28. int vis[1010][1010];
  29. void dfsearch(int fir,int x,int y,int dep);
  30. int f(int x,int y,int mid){return cnt2+x*mid+y;}
  31. int min(int a,int b){return a>b?b:a;}
  32. int main(){
  33. int i,j,T,k;
  34. char a[100]={0};
  35. scanf("%d",&T);
  36. while(T--){
  37. cnt1=cnt2=0;
  38. memset(matrix,0x3f,sizeof(matrix));
  39. scanf("%d%d",&n,&m);
  40. for(i=0;i<n;i++){
  41. scanf("%s",a);
  42. for(j=0;j<m;j++){
  43. if(a[j]=='X')state[i][j]=0;
  44. else if(a[j]=='E')state[i][j]=1,e[cnt1].x=i,e[cnt1].y=j,number[i][j]=cnt1,cnt1++;
  45. else state[i][j]=2,p[cnt2].x=i,p[cnt2].y=j,number[i][j]=cnt2,cnt2++;
  46. }
  47. }
  48. //先判断有没有出不去的,同时fill matrix
  49. for(i=0;i<cnt2;i++){//人
  50. memset(vis,0x3f,sizeof(vis));
  51. dfsearch(i,p[i].x,p[i].y,0);
  52. int flag=0;
  53. for(j=0;j<cnt1;j++)if(matrix[i][j]<inf){
  54. flag=1;break;
  55. }
  56. if(!flag){
  57. printf("Oh, poor single dog!\n");
  58. break;
  59. }
  60. }
  61. if(!flag)continue;
  62. //再二分答案,判断mid可行不可行
  63. int left=1,right=cnt2;
  64. while(left<right){
  65. int mid=left+right>>1;
  66. //跑最大流=点数,可行
  67. cnt=2;
  68. memset(head,0,sizeof(head));
  69. memset(cur,0,sizeof(cur));
  70. for(i=0;i<cnt2;i++){
  71. for(j=0;j<cnt1;j++){
  72. if(matrix[i][j]<=mid){//人->E
  73. for(k=matrix[i][j];k<=mid;k++){
  74. add(f(j,k,mid),i+3,0);
  75. add(i+3,f(j,k,mid),1);
  76. }
  77. }
  78. }
  79. add(i+3,s,0);//源->人
  80. add(s,i+3,1);
  81. }
  82. for(i=0;i<cnt1;i++){
  83. for(j=1;j<mid;j++){
  84. add(f(i,j,mid),f(i,j+1,mid),1);
  85. add(f(i,j+1,mid),f(i,j,mid),0);
  86. add(f(i,j,mid),t,1);
  87. add(t,f(i,j,mid),0);
  88. }
  89. add(f(i,mid,mid),t,1);
  90. add(t,f(i,mid,mid),0);
  91. }
  92. int din=dinic();
  93. if(din<cnt2)left=mid+1;
  94. else right=mid;
  95. }
  96. printf("%d\n",left);
  97. }
  98. return 0;
  99. }
  100. void dfsearch(int fir,int x,int y,int dep){
  101. int i;
  102. if(x<0||y<0||x>=n||y>=m)return;
  103. if(vis[x][y]<=dep)return;
  104. vis[x][y]=dep;
  105. if(!state[x][y])return;//墙
  106. if(state[x][y]==1){
  107. if(dep<matrix[fir][number[x][y]])matrix[fir][number[x][y]]=dep;
  108. else return;//必然无法增广更优路线
  109. }
  110. dfsearch(fir,x+1,y,dep+1);
  111. dfsearch(fir,x-1,y,dep+1);
  112. dfsearch(fir,x,y-1,dep+1);
  113. dfsearch(fir,x,y+1,dep+1);
  114. }
  115. int dinic(){
  116. int ans=0,p,i;
  117. while(bfs()){
  118. while((p=dfs(s,inf)))ans+=p;
  119. for(i=0;i<160000;i++)cur[i]=head[i];
  120. }
  121. return ans;
  122. }
  123. int bfs(){
  124. memset(queue,0,sizeof(queue));
  125. memset(depth,0,sizeof(depth));
  126. int hd=0,tl=0;
  127. queue[tl++]=s;
  128. depth[s]=1;
  129. while(hd<tl){
  130. int i;
  131. for(i=head[queue[hd]];i;i=edge[i].near){
  132. int p=edge[i].end;
  133. if(edge[i].len&&!depth[p]){
  134. depth[p]=depth[queue[hd]]+1;
  135. queue[tl++]=p;
  136. }
  137. }
  138. hd++;
  139. }
  140. return depth[t];
  141. }
  142. int dfs(int p,int flow){
  143. if(p==t)return flow;
  144. int i;
  145. for(i=cur[p];i;i=edge[i].near){
  146. cur[p]=i;
  147. int k=edge[i].end;
  148. if(depth[k]==depth[p]+1&&edge[i].len){
  149. int ans=dfs(k,min(flow,edge[i].len));
  150. if(ans){
  151. edge[i].len-=ans;
  152. edge[i^1].len+=ans;
  153. return ans;
  154. }
  155. }
  156. }
  157. return 0;
  158. }

(在正解的基础上有不影响整体代码结构的修改,交上并不能AC)

2017算法期末复习练习赛-G Beihang Couple Pairing Comunity 2017 题解(网络流)的更多相关文章

  1. 2016级算法期末模拟练习赛-E.AlvinZH的青春记忆III

    1083 AlvinZH的青春记忆III 思路 难题,二分图. 说这是一个考察二分图的题目,你可以会说"不可能",这哪里像一个二分图了!这真的是一个二分图,考察的是最小顶点覆盖. ...

  2. 2016级算法期末模拟练习赛-F.AlvinZH的青春记忆IV

    1086 AlvinZH的青春记忆IV 思路 难题,动态规划. 这是一道很有意思的题,因为它不仅卡了时间,也卡了空间,而且卡的很妙很迷. 光是理解题意已经有点难度,简化题意:两串数字序列,相等的数字定 ...

  3. 2016级算法期末模拟练习赛-D.AlvinZH的序列问题

    1111 AlvinZH的序列问题 思路 中等题,动态规划. 简化题意,. 坑点一:二维int数组MLE,明显会超过内存限制,由于\(n\)最大为1e4,那么我们的dp数组最大也是1e4,考虑使用sh ...

  4. 2016级算法期末模拟练习赛-B.AlvinZH的青春记忆I

    1083 AlvinZH的青春记忆I 思路 中等题,动态规划. 简化题意,一个环上取数,数不可相邻,取取得数之和最大值. 环不好表示,可以解开变成一列数,那么答案应为下列两种情况较大者. ①:取第一个 ...

  5. 2016级算法期末模拟练习赛-C.AlvinZH的青春记忆II

    1084 AlvinZH的青春记忆II 思路 中等题,二分. 简化题意,一列数字,每秒会自动-1,特殊操作可以使一个数在1s内-k,问这些数都减至0需要多久. 答案肯定在[1,xMax]之间,采用二分 ...

  6. 2016级算法期末模拟练习赛-A.wuli51和京导的毕业旅行

    1063 wuli51和京导的毕业旅行 思路 中等题,二分+贪心. 简化题意,将m+1个数字分成n份,ans为这n段中每段数字和的最大值,求ans最小值及其方案. 对于这种求最小的最大值,最常用的方法 ...

  7. 2016级算法第五次上机-A.Beihang Collegiate Pronunciation Contest 2017

    1065 Beihang Collegiate Pronunciation Contest 2017 思路 在字符串中不断做匹配 找到一个匹配就输出 时间复杂度\(O(n)\) ps.模式串是定长的, ...

  8. SCE信号期末复习省流小助手(懒人版)

    XDU-SCE网信院信号期末复习省流小助手(懒人版) 本人根据西安电子科技大学网络与信息安全18年期末考试整理的考点和题型 以下题型代表了信号与系统课程的 精髓 若能掌握以下知识点和题型,80分稳有: ...

  9. JavaEE期末复习

    期末复习 基础 jsp技术中嵌入java代码,使用的符号 <%%> 掌握jsp技术中引用其他标签库指令标签的书写 掌握jsp技术中request对象setAttribute( ).setC ...

随机推荐

  1. POJ - 3665 icow

    Fatigued by the endless toils of farming, Farmer John has decided to try his hand in the MP3 player ...

  2. 记一次getshell

    水文涉及的知识点: Oday的挖掘 可以执行命令,但是有WAF , 命令执行的绕过 机器不出网,无法反弹 Echo写文件,发现只要写入php文件,后缀就重名为*,如1.php 变成1.* 通过上传 l ...

  3. sass文件编译(.scss->.css),使用ruby环境,在windows10,koala工具,Error: Invalid GBK character "\xE5"

    1 注意事项: 问题描述: 请确保 Encoding.default_external = Encoding.find('utf-8') 是uft-8 编码! sass文件编译时候使用ruby环境,在 ...

  4. TypeScript 1.7 & TypeScript 1.8

    TypeScript 1.7 & TypeScript 1.8 1 1 https://zh.wikipedia.org/wiki/TypeScript TypeScript是一种由微软开发的 ...

  5. LeetCode & Binary Search 解题模版

    LeetCode & Binary Search 解题模版 In computer science, binary search, also known as half-interval se ...

  6. 专利 & 发明专利 & 专利查询

    专利 & 发明专利 & 专利查询 PDF 文档中表格解析的方法.系统.存储介质及电子设备 中国专利公布公告 http://epub.sipo.gov.cn/index.action 中 ...

  7. let & var & initialized bug

    let & var & initialized bug what's wrong with this? https://github.com/lydiahallie/javascrip ...

  8. js navigator.wakeLock 保持屏幕唤醒状态

    let lock; btn.addEventListener("click", async () => { try { if (lock) { lock.release(); ...

  9. C++ 中的智能指针-基础

    简介 在现代 C++ 编程中,标准库包含了智能指针(Smart pointers). 智能指针用来确保程序不会出现内存和资源的泄漏,并且是"异常安全"(exception-safe ...

  10. VAST重磅出击,NGK网络搜索量超越ETH!

    Wechat指数中,NGK超越ETH,NGK搜索指数是157648点位,单日环比上涨11.95%,ETH搜索指数是115604点位,就连区块链标杆的BTC也仅仅只有171669点位,我们可清楚的看到N ...