2017暑假集训 div1 匹配问题(1)
来源:互联网 发布:淘宝福袋是死人衣服 编辑:程序博客网 时间:2024/05/29 07:46
A ZOJ 1002
题意:放棋子,棋子横竖不能相见。
做法:直接暴力DFS就行了。n很小只有4
B HDU2444
题意:给定n个学生,他们之间可能互相认识,首先判断能不能将这些学生分为两组,使组内学生不认识;
现想将学生两两分组,且保证每一组的学生都认识,这样分组可达到的最大组数为多大?
做法:首先用BFS染色判断是否是二分图,相连的边异色,如果不是则直接NO就行了。然后跑一边匈牙利算法,最后问的是几对可以匹配成功,那答案输出的时候要除以2
#include <iostream>#include <stdio.h>#include <string.h>#include <vector>#include <string.h>#include <queue>using namespace std;const int maxn=250;int n,m;vector<int> g[maxn];int col[maxn];bool judge(){ memset(col,-1,sizeof(col)); col[1]=1; queue<int> que; que.push(1); while(!que.empty()) { int u=que.front(); que.pop(); int next=1-col[u]; for(int i=0;i<g[u].size();i++) { int v=g[u][i]; if(col[v]==-1) { col[v]=next; que.push(v); } else { if(col[v]==col[u]) return 1; } } } return 0;}bool used[maxn];int boy[maxn];bool f(int u){ for(int i=0;i<g[u].size();++i) { int v=g[u][i]; if(used[v]) continue; used[v]=1; if(boy[v]==-1||f(boy[v])) { boy[v]=u; return 1; } } return 0;}int main(){ while(scanf("%d%d",&n,&m)!=EOF) { for(int i=1;i<=n;++i) g[i].clear(); for(int i=1;i<=m;++i) { int a,b; scanf("%d%d",&a,&b); g[a].push_back(b); g[b].push_back(a); } if(judge()) printf("No\n"); else { int ans=0; memset(boy,-1,sizeof(boy)); for(int i=1;i<=n;++i) { memset(used,0,sizeof(used)); if(f(i)) ans++; } printf("%d\n",ans/2); } } return 0;}
HDU 1083
题意:有p门课程,n个学生,每个学生在一个committee代表一门课程,求学生和课程的最大匹配,若最大匹配等于p,输出YES,否则输出NO。
做法:直接二分匹配上模板
HDU 1281
题意:中文题意
做法:建图是关键,把x看做一个集合,y看做一个集合(x,y)代表x可以连y。然后跑一边最大匹配,然后枚举所有边,如果跑出来的答案与第一次不一样就是关键点,
#include <iostream>#include <stdio.h>#include <algorithm>#include <string.h>#include <queue>#include <vector>using namespace std;int n,m,k;bool map[105][105];struct node{ int x,y;}p[100*105];int boy[150];int used[150];bool f(int u){ for(int i=1;i<=m;++i) { if(map[u][i]&&!used[i]) { used[i]=1; if(boy[i]==-1 || f(boy[i])) { boy[i]=u; return 1; } } } return 0;}int pp(){ memset(boy,-1,sizeof(boy)); int l=0; for(int i=1;i<=n;++i) { memset(used,0,sizeof(used)); if(f(i)) l++; } return l;}int main(){ int kiss=0; while(scanf("%d%d%d",&n,&m,&k)!=EOF) { memset(map,0,sizeof(map)); for(int i=1;i<=k;++i) { scanf("%d%d",&p[i].x,&p[i].y); map[p[i].x][p[i].y]=1; } int l=pp(); int flag=0; for(int i=1;i<=k;++i) { map[p[i].x][p[i].y]=0; int temp=pp(); if(temp!=l) flag++; map[p[i].x][p[i].y]=1; } printf("Board %d have %d important blanks for %d chessmen.\n",++kiss,flag,l); } return 0;}
HDU 2819
题意:交换图的某些行或者是某些列(可以都换),使得这个N*N的图对角线上全部都是1.
注意:这里有一点需要说明,就是说题目的交换,其实是将原来图的某一行移到最后图的某一行,而不是指先交换两行,得到一个新图,再交换新图的两行。感觉这里比较坑。
这里先说明的一点就是,如果通过交换某些行没有办法的到解的话,那么只交换列 或者 既交换行又交换列 那也没办法得到解。这个可以用矩阵的秩来解释,所有的对角线都是1,所以也就是矩阵的秩就是N,所以秩小于N就无解。另外,根据矩阵的性质,任意交换矩阵的两行 或者 两列,矩阵的秩不变,也就保证了如果通过 只交换行 或 只交换列 无法得到解的话,那么其他交换形式也必然无解。
做法: 既然说是用二分图的最大匹配,那怎么构建二分图呢,我们构建的二分图,第一部分X表示的是横坐标,第二部分Y表示纵坐标,所以范围都是1~N,然后如果a[i][j]是1,那我们就从X的i向Y的j引一条边,那么这条边的含义就可以解释为可以将Y的第j列(因为Y表示的是列的集合)移到第i列,使得a[i][i]变成1,这样就相当于是第i行第i列就变成了1,也就是说对角线多了一个1。
因此我们求这个二分图的最大匹配(目的是为了让每一列只与X中的某一行匹配),这样来就形成了N条边,那我们只需要将所有匹配的边的右边(列) 和 左边(行)所在的列 交换,这样一来对角线上这一行就成了1.
上面也也正好提示了如果最大匹配是N,那就存在解,否则无解。
这里先说明的一点就是,如果通过交换某些行没有办法的到解的话,那么只交换列 或者 既交换行又交换列 那也没办法得到解。这个可以用矩阵的秩来解释,所有的对角线都是1,所以也就是矩阵的秩就是N,所以秩小于N就无解。另外,根据矩阵的性质,任意交换矩阵的两行 或者 两列,矩阵的秩不变,也就保证了如果通过 只交换行 或 只交换列 无法得到解的话,那么其他交换形式也必然无解。
做法: 既然说是用二分图的最大匹配,那怎么构建二分图呢,我们构建的二分图,第一部分X表示的是横坐标,第二部分Y表示纵坐标,所以范围都是1~N,然后如果a[i][j]是1,那我们就从X的i向Y的j引一条边,那么这条边的含义就可以解释为可以将Y的第j列(因为Y表示的是列的集合)移到第i列,使得a[i][i]变成1,这样就相当于是第i行第i列就变成了1,也就是说对角线多了一个1。
因此我们求这个二分图的最大匹配(目的是为了让每一列只与X中的某一行匹配),这样来就形成了N条边,那我们只需要将所有匹配的边的右边(列) 和 左边(行)所在的列 交换,这样一来对角线上这一行就成了1.
上面也也正好提示了如果最大匹配是N,那就存在解,否则无解。
#include <iostream>#include <stdio.h>#include <string.h>#include <algorithm>#include <queue>using namespace std;bool map[110][110];int boy[110];int used[110];int n;int x[2000],y[2000];bool f(int u){ for(int i=1;i<=n;++i) { if(map[u][i]&&!used[i]) { used[i]=1; if(boy[i]==-1 || f(boy[i]) ) { boy[i]=u; return 1; } } } return 0;}int main(){ while(scanf("%d",&n)!=EOF) { memset(map,0,sizeof(map)); for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { int c; scanf("%d",&c); if(c) map[i][j]=1; } } memset(boy,-1,sizeof(boy)); int ans=0; for(int i=1;i<=n;++i) { memset(used,0,sizeof(used)); if(f(i)) ans++; } if(ans<n) { printf("-1\n"); continue; } else { int ans=0; for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { if(boy[j]==i&&i!=j) { x[ans]=i; y[ans++]=j; swap(boy[i],boy[j]); break; } } } printf("%d\n",ans); for(int i=0;i<ans;++i) { printf("C %d %d\n",x[i],y[i]); } } } return 0;}
阅读全文
0 0
- 2017暑假集训 div1 匹配问题(1)
- 2017暑假集训 div1 匹配问题(1)
- 2017暑假集训 div1 DP(1)
- 2017暑假集训 div1 搜索进阶(1)
- 2017暑假集训 div1 最短路(1)
- 2017暑假集训 div1 并查集(1)
- 2017暑假集训 div1 最小生成树(1)
- 2017暑假集训 div1 线段树(1)
- 2017暑假集训 div1 连通图(1) POJ3694 &&POJ3177
- 2017暑假集训 div1 DP(2)
- 2017暑假集训 div1 最短路(2)
- 2017暑假集训 div1 最短路(3)
- 2017暑假集训 div1 并查集(2)
- 2017暑假集训 div1 线段树(2)
- 2017暑假集训 div1 连通图(2)
- 2017暑假集训 div1 简单搜索
- 2017暑假集训总结 (17.7.9-)
- 2017暑假集训总结
- opencv视频取帧并进行人脸检测(Windows和Linux双版本)
- samba简单免密搭建方式
- 剖析容器List
- CDH启用 sentry
- 51实现定时器定时调整
- 2017暑假集训 div1 匹配问题(1)
- 机器学习实战 笔记 debug(一) kNN
- 单例模式的双层锁原理
- Java GC 相关
- 一个简单的倒计时
- 【c/c++】完成端口服务器中转实现两个客户端之间通信
- 堆
- 编程调用Revit自己提供的命令
- Python学习笔记——socket通信