原题传送门

题目分析

  • 给定一张n*n的芯片。

    '.'表示该格子可以放一个零件。

    'C'表示该格子已经放了一个零件(不能拆下)。

    '/'表示该格子不能放零件。

    要求在芯片的现有基础上,放置尽可能多的零件,使得:

    1. 第i行与第i列零件数相等。
    2. 每行每列零件数<=总零件数*A/B。

条件1

  • 考虑如何使得第i行第j列零件数相等。

    首先可以想到经典的行列二分图模型,即\((i,j)\)如果可放置零件,则连边;如果必须放置零件,则记录其为必选。最后跑最大流即可。

    然而这种模型的局限性就在于无法控制行列相等。

  • 我们考虑用逆向思维解决:\((i,j)\)如果可放置零件,则连边;如果必须放置零件,则不连。而边的意义为点\((i,j)\)不放零件

    这样有什么好处呢?

    既然原始边为不放零件,那我们就可以在\((i,i)\)之间连接新边,来收集剩余流量

    如何理解?

    设第i行可放置和必须放置零件总和为\(a[i]\),而已经流出\(x\)流量表示不选,第i列同理。则我们可以用新边收集\((i,i)\)之间剩余的流量,而这里的流量就表示选择放置零件

    样例分析(不考虑条件2):

    1. n=2
    2. C.
    3. /.

    如图,行列间的曲线为表示不选的原始边,直线为表示选择的新边。

    同时由于(1,1)为'C',我们记录下它的流量并体现在选择边中,但不建立表示不选的曲线。

    则选择的最大流量为2,不选的边(1,2)流量为1,即最大流=3。

    最终答案为选择的最大流量2-已有的必选流量1=1。

  • 分析上面样例的求解过程,我们不难发现需要跑最大流,检查最大流是否等于所有可选与必选的流量和来判断是否有解(想一想为什么)。

    然而问题在于如何控制流量尽可能经过收集流量的直线边?

    可见是要在所有最大流中,选择收集流量最多的那一种,考虑用添加费用作为优先级的方式来实现。

    我们使得所有选择边的优先级为1,所有不选边的优先级为0,而优先级体现在网络流中就是费用。

    这样跑最大费用最大流即可,求出的最大流=选与不选的流量总和,最大费用=选的流量总和,答案=最大费用-已有的必选流量。

条件2

  • 解决了行列相等的问题,但题目还要求每条选择边的流量<=总费用*A/B。

    也就是说,我们需要对新建的选择边容量进行限流,使其<=总费用*A/B。

    然而总费用需要建边后求出,而建边需要总费用进行限流。(明显该算法被条件2锁死)

    因此我们不能被动地去等待总费用被求出,而应该主动枚举。

  • 考虑枚举总费用,这样就能求出选择边的容量限制,完成建图。

    跑完最大费用最大流,检查我们求出的费用是否等于所枚举的总费用值,等于则更新答案。

    题目n<=40,枚举总费用复杂度\(n^2\),费用流复杂度怎么也得比最大流的\(\sqrt n m\)要大,也就是说\(n^4\)乃至\(n^5\)的复杂度在这道题是非常危险的。

  • 考虑一种常见的优化方式:保持上次枚举费用所跑出的残留网络不动,更改选择边的容量限制,然后继续跑费用流。

    然而这种方式在这道题是行不通的,假设这次选择边的容量限制+=1,那么在继续跑费用流之前,这张图的当前流可能已经不是最大费用流了(想一想为什么)。而我们的spfa+dinic实质上是一种贪心,是必须保证当前流时刻是最大费用流的!

    可能还可以加个消圈算法来调整,但是编程难度较大,我们考虑直接改变枚举思路。

  • 当容量限制lim确定时,我们所跑出来的总费用也必然确定。

    因此,完全没有必要枚举总费用,我们只要枚举容量限制lim,然后检查总费用是否满足条件2即可。

    也就是说,\(n\)复杂度枚举容量限制lim,然后求出最大总费用maxw,只要\(lim<=maxw*A/B\)便能够满足条件2,更新答案即可。

    最终答案取所有可能解中的最大值。

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. #include<queue>
  5. #define fakemain main
  6. #define inf 0x3f3f3f3f
  7. using namespace std;
  8. const int MAXN=110;
  9. int n,s,t,A,B;
  10. char ss[MAXN][MAXN];
  11. int a[MAXN];//统计每行每列的总流量
  12. int b[MAXN];//记录每行每列的已有流量
  13. int en=-1,eh[MAXN];
  14. struct edge
  15. {
  16. int v,c,w,next;
  17. edge(int V=0,int C=0,int W=0,int N=0):v(V),c(C),w(W),next(N){}
  18. };edge e[MAXN*MAXN];
  19. inline void add_edge(int u,int v,int c,int w)
  20. {
  21. e[++en]=edge(v,c,w,eh[u]);eh[u]=en;
  22. e[++en]=edge(u,0,-w,eh[v]);eh[v]=en;
  23. }
  24. void input()
  25. {
  26. s=n*2+1;t=n*2+2;
  27. memset(a,0,sizeof(a));
  28. memset(b,0,sizeof(b));
  29. for(int i=1;i<=n;++i)
  30. {
  31. scanf("%s",ss[i]+1);
  32. for(int j=1;j<=n;++j)
  33. {
  34. if(ss[i][j]=='C' || ss[i][j]=='.'){++a[i];++a[j+n];}
  35. if(ss[i][j]=='C'){++b[i];++b[j+n];}
  36. }
  37. }
  38. en=-1;
  39. memset(eh,-1,sizeof(eh));
  40. for(int i=1;i<=n;++i)add_edge(i,i+n,0,-1);//添加可选取边,初始容量为0
  41. for(int i=1;i<=n;++i)
  42. for(int j=1;j<=n;++j)if(ss[i][j]=='.')//添加不选边
  43. add_edge(i,j+n,1,0);
  44. for(int i=1;i<=n;++i)//添加源汇边
  45. {
  46. add_edge(s,i,a[i],0);
  47. add_edge(i+n,t,a[i+n],0);
  48. }
  49. }
  50. int tota,totb,rstf,lim;//总流量总和,已使用流量总和,源点流量,可选取边容量限制
  51. int maxw,ans;
  52. void init()//容量初始化
  53. {
  54. for(int i=1;i<=n;++i){e[i*2-2].c=lim;e[i*2-1].c=0;}
  55. for(int i=n+1;i<=(en+1)/2;++i)
  56. {
  57. e[i*2-2].c+=e[i*2-1].c;
  58. e[i*2-1].c=0;
  59. }
  60. }
  61. int dis[MAXN],cur[MAXN];
  62. bool inq[MAXN];
  63. bool vis[MAXN];
  64. deque<int> q;
  65. bool spfa()
  66. {
  67. fill(dis+1,dis+t+1,inf);
  68. dis[s]=0;q.push_back(s);inq[s]=1;
  69. int u,v,w;
  70. while(!q.empty())
  71. {
  72. u=q.front();q.pop_front();inq[u]=0;
  73. for(int i=eh[u];i!=-1;i=e[i].next)if(e[i].c)
  74. {
  75. v=e[i].v;w=e[i].w;
  76. if(dis[v]>dis[u]+w)
  77. {
  78. dis[v]=dis[u]+w;
  79. if(!inq[v])
  80. {
  81. inq[v]=1;
  82. if(!q.empty() && dis[v]<dis[q.front()])q.push_front(v);
  83. else q.push_back(v);
  84. }
  85. }
  86. }
  87. }
  88. return dis[t]!=inf;
  89. }
  90. int dfs(int u,int flow)
  91. {
  92. if(u==t)
  93. {
  94. maxw+=-dis[t]*flow;
  95. return flow;
  96. }
  97. vis[u]=1;
  98. int v,f,used=0;
  99. for(int i=cur[u];i!=-1;i=e[i].next)if(e[i].c)
  100. {
  101. cur[u]=i;
  102. v=e[i].v;
  103. if(dis[u]+e[i].w==dis[v] && !vis[v])
  104. {
  105. f=dfs(v,min(flow-used,e[i].c));
  106. e[i].c-=f;
  107. e[i^1].c+=f;
  108. used+=f;
  109. if(used==flow)break;
  110. }
  111. }
  112. vis[u]=0;
  113. return used;
  114. }
  115. void solve()
  116. {
  117. ans=-inf;tota=0;totb=0;
  118. for(int i=1;i<=n;++i){tota+=a[i];totb+=b[i];}
  119. for(lim=0;lim<=n;++lim)//枚举可选取边的限制lim
  120. {
  121. init();
  122. rstf=tota;maxw=0;
  123. while(rstf && spfa())//存在未使用流量,且存在増广路
  124. {
  125. memcpy(cur,eh,sizeof(cur));
  126. rstf-=dfs(s,rstf);
  127. }
  128. //如果用尽,且lim符合散热,则为可行解,更新答案
  129. if(!rstf && lim<=maxw*A/B)ans=max(ans,maxw-totb);
  130. }
  131. }
  132. void output()
  133. {
  134. if(ans==-inf)printf("impossible\n");
  135. else printf("%d\n",ans);
  136. }
  137. int fakemain()
  138. {
  139. // freopen("in.txt","r",stdin);
  140. int cnt=0;
  141. while(1)
  142. {
  143. scanf("%d %d %d",&n,&A,&B);
  144. if(n==0)return 0;
  145. printf("Case %d: ",++cnt);
  146. input();
  147. solve();
  148. output();
  149. }
  150. }

【题解】uva1104 chips challenge的更多相关文章

  1. UVA1104 Chips Challenge

    一.题目 有一个 \(n\times n\) 的矩阵,每个元素可能是 ..C./ 的其中一种,分别表示可以放置芯片.已经放置了芯片.不能放置芯片,你可以分别决定是否可以放置芯片的位置放置芯片. 最后需 ...

  2. [2011WorldFinal]Chips Challenge[流量平衡]

    Chips Challenge Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  3. UVA1104 芯片难题 Chips Challenge

    题目链接 题意 网格上放点,有些强制放,有些不能放,有些可以放可以不放.要求: 第 \(i\) 行的点数 = 第 \(i\) 列的点数 每一行每一列的点数不超过总点数的 \(k\) 倍(\(k\) 已 ...

  4. 【UVALive - 5131】Chips Challenge(上下界循环费用流)

    Description A prominent microprocessor company has enlisted your help to lay out some interchangeabl ...

  5. 【BZOJ 2673】[Wf2011]Chips Challenge

    题目大意: 传送门 $n*n$的棋盘,有一些位置可以放棋子,有一些已经放了棋子,有一些什么都没有,也不能放,要求放置以后满足:第i行和第i列的棋子数相同,同时每行的棋子数占总数比例小于$\frac{A ...

  6. Bzoj2673 3961: [WF2011]Chips Challenge 费用流

    国际惯例题面:如果我们枚举放几个零件的话,第二个限制很容易解决,但是第一个怎么办?(好的,这么建图不可做)考虑我们枚举每行每列最多放几个零件t,然后计算零件总数sum.这样如果可行的话,则有t*B&l ...

  7. 解题:BZOJ 2673 World Final 2011 Chips Challenge

    题面 数据范围看起来很像网络流诶(滚那 因为限制多而且强,数据范围也不大,我们考虑不直接求答案,而是转化为判定问题 可以发现第二个限制相对好满足,我们直接枚举这个限制就可以.具体来说是枚举所有行中的最 ...

  8. BZOJ2673 [Wf2011]Chips Challenge 费用流 zkw费用流 网络流

    https://darkbzoj.cf/problem/2673 有一个芯片,芯片上有N*N(1≤N≤40)个插槽,可以在里面装零件. 有些插槽不能装零件,有些插槽必须装零件,剩下的插槽随意. 要求装 ...

  9. bzoj3961[WF2011]Chips Challenge

    题意 给出一个n*n的网格,有些格子必须染成黑色,有些格子必须染成白色,其他格子可以染成黑色或者白色.要求最后第i行的黑格子数目等于第i列的黑格子数目,且某一行/列的格子数目不能超过格子总数的A/B. ...

随机推荐

  1. chattr +i 用户也没法随意删除

    root用户也没法用rm随意删除文件?   前言 在你的印象中,是不是root用户就可以为所欲为呢?随便一个rm -rf *,一波骚操作走人?可能没那么容易. 先来个示例,创建一个文本文件test.t ...

  2. 括号匹配-算法详细题解LeetCode

    题目:有效的括号 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足: 左括号必须用相同类型的右括号闭合. 左括号必须以正确的顺序闭合. ...

  3. 百度编辑器UEditor不能插入视频的解决方法

    在编辑器中就可以引用优酷.腾讯视频的iframe通用代码和embed html代码:移动端一般引用iframe,可设置属性,使其适应设备.(这里,建议切换到源码模式,插入相应的视频代码embed或if ...

  4. php实现登录失败次数限制

    需求:同一个账号在同一个IP地址连续密码输错一定次数后,这个账号是会被锁定30分钟的. 实现思路: 需要一个表(user_login_info)负责记录用户登录的信息,不管登录成功还是失败都记录.并且 ...

  5. RocketMQ系列(六)批量发送与过滤

    今天我们再来看看RocketMQ的另外两个小功能,消息的批量发送和过滤.这两个小功能提升了我们使用RocketMQ的效率. 批量发送 以前我们发送消息的时候,都是一个一个的发送,这样效率比较低下.能不 ...

  6. 使用Python解密仿射密码

    新学期有一门密码学课,课上老师布置了一道密码学题,题目如下: 解密由仿射密码加密的密文“DBUHU SPANO SMPUS STMIU SBAKN OSMPU SS” 想解密这个密文,首先必须要知道仿 ...

  7. 通过char与varchar的区别,学习可变长的字符类型

    转自http://www.uphtm.com/database/232.html 在mysql教程中char与varchar的区别呢,都是用来存储字符串的,只是他们的保存方式不一样罢了,char有固定 ...

  8. 西门子S7-300 PLC视频教程(百度网盘)收集于网络-供参考学习

    百度网盘地址: 西门子300 PLC视频教程 群文件里面可以找到. 下载: https://blog.csdn.net/txwtech/article/details/93016190

  9. TLS1.2协议设计原理

    目录 前言 为什么需要TLS协议 发展历史 协议设计目标 记录协议 握手步骤 握手协议 Hello Request Client Hello Server Hello Certificate Serv ...

  10. 打个总结:Web性能优化

    前段时间优化一个公司历史老项目的Web性能,却引出了一系列的问题,让我反思良多. 我通过Chrome的Lighthouse工具可以看出一些性能参数和问题反馈,我逐一对其进行优化. 根据资源请求的不同, ...