|
ID
|
Origin
|
Title
|
||
|---|---|---|---|---|
| 61 / 72 | Problem A | HDU 1045 | Fire Net | |
| 52 / 112 | Problem B | HDU 2444 | The Accomodation of Students | |
| 45 / 86 | Problem C | HDU 1083 | Courses | |
| 44 / 63 | Problem D | HDU 1281 | 棋盘游戏 | |
| 35 / 85 | Problem E | HDU 2819 | Swap | |
| 29 / 110 | Problem F | HDU 2389 | Rain on your Parade | |
| 29 / 77 | Problem G | HDU 4185 | Oil Skimming | |
| 27 / 39 | Problem H | POJ 3020 | Antenna Placement | |
| 28 / 44 | Problem I | HDU 1054 | Strategic Game | |
| 25 / 36 | Problem J | HDU 1151 | Air Raid | |
| 26 / 64 | Problem K | POJ 2594 | Treasure Exploration | |
| 26 / 61 | Problem L | HDU 3829 | Cat VS Dog | |
| 23 / 56 | Problem M | POJ 2289 | Jamie's Contact Groups | |
| 15 / 74 | Problem N | POJ 2112 | Optimal Milking | |
| 14 / 31 | Problem O | POJ 3189 | Steady Cow Assignment | |
| 29 / 56 | Problem P | HDU 2255 | 奔小康赚大钱 | |
| 20 / 33 | Problem Q | HDU 3488 | Tour | |
| 10 / 20 | Problem R | URAL 1099 | Work Scheduling | |
| 7 / 22 | Problem S | HDU 4687 | Boke and Tsukkomi |
61 / 72 Problem A HDU 1045 Fire Net
s.二分匹配或者暴搜
52 / 112 Problem B HDU 2444 The Accomodation of Students
d.有一些学生,他们之间有些互相认识,有些不认识,其中若A和B认识,B和C认识,并不代表A和C认识,现在提供互相认识的学生编号,问是否能将这些学生分成两组,同一组的学生互相不认识,如果可以,则分配给这些学生双人间,只有是朋友才能住双人间,问最多要准备几间房
即判断是不是二分图,若是,其最大匹配数是多少?
s.首先用交叉染色法判断是不是二分图,然后用匈牙利算法求最大匹配。
c.唯一比较坑的是边数开了1w,提交G++超时!而用C++却Runtime Error,才知道可能是数组原因。。
#include<iostream> #include<stdio.h> #include<queue> #include<string.h> using namespace std; #define MAXN 205//点数 #define MAXM 41000//边数。不要吝啬内存,只要不超限。一定要开够 int color[MAXN]; int n; struct Edge{ int to,next; }edge[MAXM]; int head[MAXN]; int tot; void addedge(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } void init(){ tot=0; memset(head,-1,sizeof(head)); } bool bfs(int start){//bfs交叉染色,两种颜色标记为 1 和 -1,未染色标记为 0 int v; queue<int>q; color[start]=1; q.push(start); while(!q.empty()){ int temp=q.front(); q.pop(); for(int i=head[temp];i!=-1;i=edge[i].next){// v=edge[i].to; if(color[v]==0){//未染色 if(color[temp]==1){ color[v]=-1; } else{ color[v]=1; } q.push(v); } else{//已染色 if(color[v]==color[temp]){//相邻的两点颜色相同 return false;//不能交叉染色 } } } } return true; } //匈牙利算法 int linker[MAXN]; bool used[MAXN]; bool dfs(int u){ for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(!used[v]){ used[v]=true; if(linker[v]==-1||dfs(linker[v])){ linker[v]=u; return true; } } } return false; } int hungary(){ int res=0; memset(linker,-1,sizeof(linker)); //点的编号0~uN-1 for(int u=0;u<n;u++){ if(color[u]==-1)continue;//只看颜色是1的集合 memset(used,false,sizeof(used)); if(dfs(u))res++; } return res; } int main(){ int m; int u,v; bool flag; while(~scanf("%d",&n)){ memset(color,0,sizeof(color)); init(); scanf("%d",&m); for(int i=0;i<m;++i){ scanf("%d%d",&u,&v); addedge(u-1,v-1); addedge(v-1,u-1); } flag=true; for(int i=0;i<n;++i){ if(color[i]!=0)continue;//已经染色 if(!bfs(i)){//不是二分图 flag=false; break; } } if(flag){ printf("%d\n",hungary()); } else{ printf("No\n"); } } return 0; }
c2.这里的程序hungary()用vector实现,同时染色用的dfs
/* HDU 2444 The Accomodation of Students */ #include<iostream> #include<string.h> #include<vector> using namespace std; #define MAXN 202 vector<int>EV[MAXN]; int linker[MAXN]; bool used[MAXN]; int uN; int matchs[MAXN],cnt[MAXN]; bool dfs(int u) { int i; for(i=0;i<EV[u].size();i++) { int v=EV[u][i]; if(!used[v]) { used[v]=true; if(linker[v]==-1||dfs(linker[v])) { linker[v]=u; return true; } } } return false; } int hungary() { int res=0; int u; memset(linker,-1,sizeof(linker)); for(u=1;u<=uN;u++) { memset(used,false,sizeof(used)); if(dfs(u)) res++; } return res; } bool judge(int x,int y) { int i; for(i=0;i<EV[x].size();i++) { if(cnt[EV[x][i]]==0) { cnt[EV[x][i]]=-1*y; matchs[EV[x][i]]=true; if(!judge(EV[x][i],-1*y)) return false; } else if(cnt[EV[x][i]]==y) return false; } return true; } bool matched() { int i; memset(matchs,false,sizeof(matchs)); for(i=1;i<=uN;i++) { if(EV[i].size()&&!matchs[i]) { memset(cnt,0,sizeof(cnt)); cnt[i]=-1; matchs[i]=true; if(!judge(i,-1)) return false; } } return true; } int main() { int m; int i; int u,v; while(scanf("%d%d",&uN,&m)!=EOF) { for(i=1;i<=uN;i++) if(EV[i].size()) EV[i].clear(); while(m--) { scanf("%d%d",&u,&v); EV[u].push_back(v); EV[v].push_back(u); } if(matched()) printf("%d\n",hungary()/2); else printf("No\n"); } return 0; }
45 / 86 Problem C HDU 1083 Courses
s.最大匹配
44 / 63 Problem D HDU 1281 棋盘游戏
s.二分匹配
35 / 85 Problem E HDU 2819 Swap
s.二分匹配,记录过程
29 / 110 Problem F HDU 2389 Rain on your Parade
s.先用匈牙利算法的DFS版本,一直超时,
29 / 77 Problem G HDU 4185 Oil Skimming
s.二分匹配
27 / 39 Problem H POJ 3020 Antenna Placement
s.二分匹配,主要是建图
28 / 44 Problem I HDU 1054 Strategic Game
25 / 36 Problem J HDU 1151 Air Raid
d.一个有向无环图(DAG),M个点,K条有向边,求DAG的最小路径覆盖数
s.DAG的最小路径覆盖数=DAG图中的点数-相应二分图中的最大匹配数
/* 顶点编号从0开始的 邻接矩阵(匈牙利算法) 二分图匹配(匈牙利算法的DFS实现)(邻接矩阵形式) 初始化:g[][]两边顶点的划分情况 建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配 g没有边相连则初始化为0 uN是匹配左边的顶点数,vN是匹配右边的顶点数 左边是X集,右边是Y集 调用:res=hungary();输出最大匹配数 优点:适用于稠密图,DFS找增广路,实现简洁易于理解 时间复杂度:O(VE) */ #include<iostream> #include<stdio.h> #include<string.h> using namespace std; const int MAXN=512; int uN,vN;//u,v 的数目,使用前面必须赋值 int g[MAXN][MAXN];//邻接矩阵,记得初始化 int linker[MAXN];//linker[v]=u,表示v(右边Y集合中的点)连接到u(左边X集合中的点) bool used[MAXN]; bool dfs(int u){//判断以X集合中的节点u为起点的增广路径是否存在 for(int v=0;v<vN;v++)//枚举右边Y集合中的点 if(g[u][v]&&!used[v]){//搜索Y集合中所有与u相连的未访问点v used[v]=true;//访问节点v if(linker[v]==-1||dfs(linker[v])){//是否存在增广路径 //若v是未盖点(linker[v]==-1表示没有与v相连的点,即v是未盖点),找到增广路径 //或者存在从与v相连的匹配点linker[v]出发的增广路径 linker[v]=u;//设定(u,v)为匹配边,v连接到u return true;//返回找到增广路径 } } return false; } int hungary(){//返回最大匹配数(即最多的匹配边的条数) int res=0;//最大匹配数 memset(linker,-1,sizeof(linker));//匹配边集初始化为空 for(int u=0;u<uN;u++){//找X集合中的点的增广路 memset(used,false,sizeof(used));//设Y集合中的所有节点的未访问标志 if(dfs(u))res++;//找到增广路,匹配数(即匹配边的条数)+1 } return res; } int main(){ int i,ans; int K,M; int u,v; int T; scanf("%d",&T); while(T--){ scanf("%d%d",&M,&K); uN=M;//匹配左边的顶点数 vN=M;//匹配右边的顶点数 memset(g,0,sizeof(g));//二分图的邻接矩阵初始化 for(i=0;i<K;++i){ scanf("%d%d",&u,&v); g[--u][--v]=1;//顶点编号从0开始的 } ans=M-hungary(); printf("%d\n",ans); } return 0; }
26 / 64 Problem K POJ 2594 Treasure Exploration
26 / 61 Problem L HDU 3829 Cat VS Dog
最大独立集
23 / 56 Problem M POJ 2289 Jamie's Contact Groups
多重匹配+二分
貌似也可以用最大流+二分做
d.Jamie有很多联系人,但是很不方便管理,他想把这些联系人分成组,已知这些联系人可以被分到哪个组中去,而且要求每个组的联系人上限最小,即有一整数k,使每个组的联系人数都不大于k,问这个k最小是多少?
s.一对多的二分图的多重匹配。二分图的多重匹配算法的实现类似于匈牙利算法,对于集合x中的元素xi,找到一个与其相连的元素yi后,检查匈牙利算法的两个条件是否成立,若yi未被匹配,则将
xi,yi匹配。否则,如果与yi匹配的元素已经达到上限,那么在所有与yi匹配的元素中选择一个元素,检查是否能找到一条增广路径,如果能,则让出位置,让xi与yi匹配。
二分求出limit,知道找到可以构成多重匹配的最小限制limit,在main函数中二分搜索。
#include<stdio.h> #include<string.h> #include<queue> #include<algorithm> using namespace std; #define N 1010 int vis[N], maps[N][N], ans, n, m; struct node { int cnt;///和yi相匹配的个数; int k[N];///和yi相匹配的x的集合; }Linky[N]; bool Find(int u, int limit) { for(int i=1; i<=m; i++) { if(!vis[i] && maps[u][i]) { vis[i] = 1; if(Linky[i].cnt < limit) { Linky[i].k[ Linky[i].cnt++ ] = u; return true; } for(int j=0; j<Linky[i].cnt; j++) { if(Find( Linky[i].k[j], limit )) { Linky[i].k[j] = u; return true; } } } } return false; } bool xyl(int limit)///匈牙利算法; { memset(Linky, 0, sizeof(Linky)); for(int i=1; i<=n; i++) { memset(vis, 0, sizeof(vis)); if(!Find(i, limit))///当前的limit让i没有匹配,所以不能用limit; return false; } return true; } int main() { int x; char s[20], ch; while(scanf("%d %d", &n, &m), m+n) { memset(maps, 0, sizeof(maps)); for(int i=1; i<=n; i++) { scanf("%s", s); while(1) { scanf("%d%c", &x, &ch); maps[i][x+1] = 1; if(ch == '\n') break; } } int L = 1, R = n; ans = n; while(L <= R) { int mid = (L+R)/2; if(xyl(mid))///如果当前mid满足题意; { R = mid-1; ans = mid; } else L = mid+1; } printf("%d\n", ans); } return 0; }