Typesetting math: 100%
Evanyou Blog 彩带

P3153 [CQOI2009]跳舞

题目描述

一次舞会有n个男孩和n个女孩。每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞。每个男孩都不会和同一个女孩跳两首(或更多)舞曲。有一些男孩女孩相互喜欢,而其他相互不喜欢(不会”单向喜欢“)。每个男孩最多只愿意和k个不喜欢的女孩跳舞,而每个女孩也最多只愿意和k个不喜欢的男孩跳舞。给出每对男孩女孩是否相互喜欢的信息,舞会最多能有几首舞曲?

输入输出格式

输入格式:

第一行包含两个整数n和k。以下n行每行包含n个字符,其中第i行第j个字符为'Y'当且仅当男孩i和女孩j相互喜欢。

输出格式:

仅一个数,即舞曲数目的最大值。

输入输出样例

输入样例#1: 
  1. 3 0
  2. YYY
  3. YYY
  4. YYY
输出样例#1: 
  1. 3

说明

N<=50 K<=30

 

Solution:

  本题太毒,调了几天,终于又填完坑了~

  像这种需要配对,而且数据还这么小的题目,一眼就容易想到网络最大流。

  那么如果直接去跑最大流的话,显然不可行。

  题意中说相同的两个人只能搭配一次,那么最多也就50次,很容易想到从大到小枚举天数然后跑最大流判断(我写了下枚举+最大流,事实证明是可以过的),但是,本题有很明显的单调性,即若前i天可以完整搭配,则答案一定在[i,n]之间,否则就在[0,i1]之间。于是考虑二分答案,然后跑最大流check

  再来考虑最大流check是否可行。每个男生的点和女生的点相匹配,只有两种情况,要么不互相喜欢使用1次限制,要么互相喜欢不需要花费。

  因为每人最多和不喜欢的匹配k次,于是我们将每个学生都拆成两个点,之间连边为k表示限制,假设男生a被拆为a1,a2a1a的全局,a2是与a不互相喜欢的分点),女生b被拆为b1,b2(类比男生的含义),每次二分的天数x,重新建图:sa1连容量为xs为源点,该边表示每个人应该匹配x次),a1a2连容量为k(表示a最多和k个不喜欢的女生匹配),b1,b2类比男生连法(b2b1b1t)。每次若男生a和女生b不喜欢,连容量为1的边a2b2,若ab互相喜欢,则应直接连容量为1的边a1b1

  然后每次跑完最大流后,看最大流是否等于xn,便能判断是否成立。(最后需要注意的是二分的边界值:l=0,r=n,最少就是1次也无法搭配,最多就是n人互相搭配一次)

代码:

复制代码
  1. #include<bits/stdc++.h>
  2. #define il inline
  3. #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
  4. #define Min(a,b) ((a)>(b)?(b):(a))
  5. #define debug printf("%d %s\n",__LINE__,__FUNCTION__)
  6. using namespace std;
  7. const int N=100005,inf=23333333;
  8. int s,t=5200,ans,dis[10005],n,k,to[N],net[N],h[10010],cnt=1,w[N];
  9. bool mp[55][55];
  10. il void add(int u,int v,int c){to[++cnt]=v,net[cnt]=h[u],h[u]=cnt,w[cnt]=c;}
  11. il bool bfs(){
  12. queue<int>q;
  13. memset(dis,-1,sizeof(dis));
  14. q.push(s),dis[s]=0;
  15. while(!q.empty()){
  16. int u=q.front();q.pop();
  17. for(int i=h[u];i;i=net[i])
  18. if(dis[to[i]]==-1&&w[i]>0)dis[to[i]]=dis[u]+1,q.push(to[i]);
  19. }
  20. return dis[t]!=-1;
  21. }
  22. il int dfs(int u,int op){
  23. if(u==t)return op;
  24. int flow=0,used=0;
  25. for(int i=h[u];i;i=net[i]){
  26. int v=to[i];
  27. if(dis[v]==dis[u]+1&&w[i]>0){
  28. used=dfs(v,Min(w[i],op));
  29. if(!used)continue;
  30. flow+=used,op-=used;
  31. w[i]-=used,w[i^1]+=used;
  32. if(!op)break;
  33. }
  34. }
  35. if(!flow)dis[u]=-1;
  36. return flow;
  37. }
  38. il bool check(int x){
  39. memset(h,0,sizeof(h));
  40. cnt=1;
  41. For(i,1,n){
  42. add(s,i,x),add(i,s,0);
  43. add(i,i+n,k),add(i+n,i,0);
  44. add(i+n*3,t,x),add(t,i+n*3,0);
  45. add(i+n*2,i+n*3,k),add(i+n*3,i+n*2,0);
  46. }
  47. For(i,1,n) For(j,1,n){
  48. if(mp[i][j])add(i,j+3*n,1),add(j+3*n,i,0);
  49. else add(i+n,j+2*n,1),add(j+2*n,i+n,0);
  50. }
  51. int tot=0;
  52. while(bfs())tot+=dfs(s,inf);
  53. if(tot==n*x)return 1;
  54. return 0;
  55. }
  56. int main(){
  57. ios::sync_with_stdio(0);
  58. cin>>n>>k;
  59. char p;
  60. For(i,1,n) For(j,1,n) {
  61. cin>>p;
  62. if(p=='Y')mp[i][j]=1;
  63. if(n==1&&(p=='Y'||k>=1)){cout<<1;return 0;}
  64. }
  65. int mid,l=0,r=n;
  66. while(l<=r){
  67. mid=l+r>>1;
  68. if(check(mid))l=mid+1,ans=mid;
  69. else r=mid-1;
  70. }
  71. cout<<ans;
  72. return 0;
  73. }
复制代码

 

 

 

 

 

 

posted @   five20  阅读(198)  评论(0编辑  收藏  举报
编辑推荐:
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
阅读排行:
· 如何给本地部署的DeepSeek投喂数据,让他更懂你
· 超详细,DeepSeek 接入PyCharm实现AI编程!(支持本地部署DeepSeek及官方Dee
· 用 DeepSeek 给对象做个网站,她一定感动坏了
· .NET 8.0 + Linux 香橙派,实现高效的 IoT 数据采集与控制解决方案
· .NET中 泛型 + 依赖注入 的实现与应用
Live2D
欢迎阅读『P3153 [CQOI2009]跳舞』
点击右上角即可分享
微信分享提示