20211011二分图匹配+判定
20211019 今天打模板发现自己的代码居然打错了,太丢脸了~~~
怪不得wa了一个小时
----------------分割线--------------------
今天又是摸鱼的一天,就复习了一下数据结构,然后树剖板子打炸了,线段树2调疯了,tarjan也搞没了。
还有就是学了一下二分图,之前假期发了学案懒得看,今天就跑过去听,感觉还不错。
1.二分图是什么:
二分图又称作二部图,是图论中的一种。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。
简单来说,就是能把定点分成两部分,任意两点间有且只有一条路径。
至于二分图的判定,我们用深度搜索染色来实现。
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int n,m,color[205]; 5 vector<int> node[205];//领接表存图 6 7 bool dfs(int v,int c){//搜索染色 8 color[v]=c; 9 for(int i=0;i<node[v].size();i++){ 10 if(color[node[v][i]]==c){//相连的点颜色必须不同 11 return 0; 12 }else if(color[node[v][i]]==0&&!dfs(node[v][i],-c)){//还未染色切染了色也没有问题 13 return 0; 14 } 15 } 16 return 1; 17 } 18 int main(){ 19 int u,v; 20 cin>>n>>m; 21 for(int i=0;i<m;i++){ 22 cin>>u>>v; 23 node[u].push_back(v); 24 node[v].push_back(u); 25 } 26 27 for(int i=0;i<n;i++){ 28 if(color[i]==0){//从每一个点开始 29 if(!dfs(i,1)){ 30 cout<<"NO"<<endl; 31 return 0; 32 } 33 } 34 } 35 cout<<"YES"<<endl; 36 37 38 return 0; 39 }
例题:关押罪犯https://www.luogu.com.cn/problem/P1525
思路:二分答案+染色搜索判定
先排序按照暴力事件的大小升序排列,然后二分一个答案,如果MID满足条件那么大于mid的都可以,接下来就向左半边二分。
我们每找到一个mid的值就用一个check函数判断是否合法。判断方式就是,把大于mid的边存入领接表,再用染色判断大于他的边能否形成一个二分图。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N=20010; 5 const int M=100010; 6 int n,m,color[N]; 7 bool flag; 8 vector<int> e[N]; 9 struct edges{ 10 int x,y,v; 11 }a[M]; 12 13 bool cmp(edges a,edges b){ 14 return a.v<b.v; 15 } 16 17 //void dfs(int u,int c){ 18 // color[u]=c; 19 // for(int i=0;i<e[i].size();i++){ 20 // int v=e[u][i]; 21 // if(!color[v]){ 22 // dfs(v,-c); 23 // }else 24 // if(color[v]==c){ 25 // flag=0; 26 // } 27 // } 28 //} 29 void dfs(int u, int c) //染色 30 { 31 color[u]=c; 32 for(int i=0;i<e[u].size();i++) //枚举从u出 33 { 34 int v=e[u][i]; //v是u的相邻 35 if(!color[v]) dfs(v,-c); //继续染色 36 else if(color[v]==c) flag=false; //颜色 37 } 38 } 39 40 41 //bool check(int pos){ 42 // for(int i=1;i<=n;i++) e[i].clear(); 43 // for(int i=pos+1;i<=m;i++){ 44 // e[a[i].x].push_back(a[i].y); 45 // e[a[i].y].push_back(a[i].x); 46 // } 47 // flag=true; 48 // memset(color,0,sizeof(color)); 49 // for(int i=1;i<=n;i++){ 50 // if(!color[i]){ 51 // dfs(i,1); 52 // if(!flag) return false; 53 // } 54 // } 55 // return true; 56 //} 57 58 bool check(int pos) //判断是否合法:能构成 59 { 60 for(int i=1;i<=n;i++) e[i].clear(); // 61 for(int i=pos+1;i<=m;i++) // 62 { 63 e[a[i].x].push_back(a[i].y); 64 e[a[i].y].push_back(a[i].x); 65 } 66 flag=true; 67 memset(color,0,sizeof color); 68 for(int i=1;i<=n;i++) // 69 if(!color[i]) 70 { 71 dfs(i,1); 72 if(!flag) return false; 73 } 74 return true; 75 } 76 int main() 77 { 78 cin>>n>>m; 79 for(int i=1;i<=m;i++){ 80 cin>>a[i].x>>a[i].y>>a[i].v; 81 } 82 sort(a+1,a+m+1,cmp); 83 84 int l=0; 85 int r=m; 86 int mid; 87 // while(l<r){ 88 // mid=(l+r)>>1; 89 // if(check(mid)){ 90 // r=mid; 91 // cout<<r<<' '<<123<<endl; 92 // }else l=mid+1,cout<<l<<' '<<321<<endl; 93 // } 94 while(l<r) 95 { 96 mid=(l+r)>>1; 97 if(check(mid)) 98 r=mid; 99 else 100 l=mid+1; 101 } 102 if(m==1){ 103 cout<<0<<endl; 104 } else{ 105 106 cout<<a[l].v<<endl; 107 } 108 return 0; 109 }
咕咕咕,晚自习还有十分钟下课,拼手速的时刻到了:
好的上面讲完了二分图的判定,我们再来讲二分图的最优匹配:
一般来说,二分图的匹配我们用网络流或者匈牙利算法来做,但是单纯的二分图匹配的话,匈牙利算法虽然在时间复杂度上处于劣势但是编程难度小;
至于网络流吗?蒟蒻暑假也学过,但是忘完了,也难写,一个广搜加一个深搜,还有玄学弧优化和多路增广:
接下来就是匈牙利的模板了:
就是男女匹配:
每一个女生有多个喜欢的男生(两个集合之间的连边),现在求可以撮合的最多情侣数量{单身狗微笑}
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int match[505]; 5 int used[505]; 6 int k,m,n,x,y; 7 vector<int> e[1005];//领接表~~ 8 9 bool Dfs(int x) 10 { 11 for(int i=0;i<e[x].size();i++){ 12 if(!used[e[x][i]]){//男生在 “”当前“” 状态下没有被匹配 13 used[e[x][i]]=1; 14 if(match[e[x][i]]==0||Dfs(match[e[x][i]])){//男生一直没有“女”朋友 或 该男生之前匹配的女士可以换一个男生 15 match[e[x][i]]=x;//男生换一个 16 return 1; 17 } 18 } 19 } 20 return 0; 21 } 22 int main() 23 { 24 while(scanf("%d",&k)!=EOF&&k){ 25 cin>>m>>n; 26 for(int i=1;i<=k;i++){ 27 e[i].clear(); 28 } 29 memset(match,0,sizeof(match)); 30 for(int i=1;i<=k;i++){ 31 cin>>x>>y; 32 e[x].push_back(y);//连边 33 } 34 int sum=0; 35 for(int i=1;i<=m;i++){//分别从每一个女生开始 36 memset(used,0,sizeof(used));//清空 37 if(Dfs(i)) sum++;//在不影响其他人的情况下成功了 38 } 39 cout<<sum<<endl; 40 } 41 return 0; 42 }
马上来一道有趣的例题,先自己看哟~~~
http://poj.org/problem?id=2536
不要抄题解呀
题目描述】
草原某片区域上有 N 个地鼠正在地面寻食,附近有 M 个地鼠洞,地鼠和地鼠洞的当前位置用坐标(x,y)表示。每个洞只能容纳一个地鼠。
一只老鹰正飞向这里,如果地鼠在 S 秒内没有进入地鼠洞,则会被老鹰吃掉。所有地鼠都以同一速度 V 进行逃生。请你帮地鼠家族设计一个优秀的逃生策略,使得损失的地鼠最少。
【输入格式】
输入包含多组数据。
每组数据的第一行包含四个不超过100的正整数:N,M,S,V。
接下来的n行,每行两个实数,表示一个地鼠的坐标;
接下来M行,每行两个实数,表示一个地鼠洞的坐标。
所有的距离单位是“米”,所有的时间单位是“秒”,所有的速度单位是“米/秒”。
【输出格式】
对每组数据输出一行,一个整数,表示至少要被老鹰吃掉的地鼠的数量。
【输入样例】
2 2 5 10
1.0 1.0
2.0 2.0
100.0 100.0
20.0 20.0
【输出样例】
1
板子题了?
在每一只地鼠he他能跑到的洞之间连一条边,在该图上用匈牙利
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,s,v; 4 struct node{ 5 double x,y; 6 }mouse[110],hole[110]; 7 double work(int i,int j){ 8 return sqrt((mouse[i].x-hole[j].x)*(mouse[i].x-hole[j].x)+(mouse[i].y-hole[j].y)*(mouse[i].y-hole[j].y)); //计算距离 9 } 10 vector<int> e[200]; 11 int used[110],match[110]; 12 13 bool Dfs(int x){ 14 for(int i=0;i<e[x].size();i++){ 15 if(!used[e[x][i]]){ 16 used[e[x][i]]=1; 17 if(match[e[x][i]]==0||Dfs(match[e[x][i]])){ 18 match[e[x][i]]=x; 19 return 1; 20 } 21 } 22 } 23 return 0; 24 } 25 int main() 26 { 27 cin>>n>>m>>s>>v; 28 for(int i=1;i<=n;i++){ 29 cin>>mouse[i].x>>mouse[i].y; 30 } 31 for(int i=1;i<=m;i++){ 32 cin>>hole[i].x>>hole[i].y; 33 } 34 for(int i=1;i<=n;i++){ 35 for(int j=1;j<=m;j++){ 36 int len=work(i,j); 37 if(len<s*v){ 38 e[i].push_back(j);//连边 39 } 40 } 41 } 42 int sum=0; 43 for(int i=1;i<=n;i++){ 44 memset(used,0,sizeof(used)); 45 if(Dfs(i)) sum++; 46 } 47 cout<<n-sum<<endl;//输出被吃掉的个数 48 return 0; 49 }
好了,这篇博客咕了一周终于搞完了,累死竞赛狗了
马上CSP了,各位rp++
拜拜(^_^)
开始颓废了