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 ",hungary()); } else{ printf("No "); } } 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 ",hungary()/2); else printf("No "); } 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 ",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 == ' ') 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 ", ans); } return 0; }
15 / 74 Problem N POJ 2112 Optimal Milking
floyd+二分+最大流
题目描述:
农场主John将他的K(1≤K≤30)个挤奶器运到牧场,在那里有C(1≤C≤200)头奶牛,在奶
牛和挤奶器之间有一组不同长度的路。K个挤奶器的位置用1~K的编号标明,奶牛的位置用K+1~
K+C的编号标明。
每台挤奶器每天最多能为M(1≤M≤15)头奶牛挤奶。
编写程序,寻找一个方案,安排每头奶牛到某个挤奶器挤奶,并使得C头奶牛需要走的所有
路程中的最大路程最小。每个测试数据中至少有一个安排方案。每条奶牛到挤奶器有多条路。
// 本题的求解算法:先用Floyd算法求出能达到的任意两点之间的最短路径,然后用Dinic算法
// 求最大流;搜索最大距离的最小值采用二分法进行。
// 建图问题 : 给个源点 s 。源点到奶牛的流量为1 如果奶牛到达挤奶器距离在max_min范围内 那没就然 该奶牛到该挤奶器的流量为1
// 再给个汇点 那么挤奶器到汇点的流量就是M 正好符合限制
/// 然后二分查找 max_min就可以了
#include <iostream> #include <algorithm> #include <queue> #include <stack> #include <math.h> #include <stdio.h> #include <string.h> using namespace std; #define MOD 1000000007 #define INF 1000000000 #define maxn 310 #define maxm 48010 #define LL __int64//long long int M,K,C,N; int cap[maxn][maxn],flow[maxn][maxn]; int dis[maxn][maxn];//,bl[1100]; void build(int max_min){ // 建图 int i,j; memset(cap,0,sizeof(cap)); memset(flow,0,sizeof(flow)); for(i=1;i<=K;i++) cap[i][N+1]=M; for(i=K+1;i<=N;i++) cap[0][i]=1; for(i=K+1;i<=N;i++) for(j=1;j<=K;j++) if(dis[i][j]<=max_min) cap[i][j]=1; } int level[maxn]; bool BFS(int s,int t){ memset(level,0,sizeof(level)); queue<int> Q; int u; int i; Q.push(s); level[s]=1; while(!Q.empty()){ u=Q.front(); Q.pop(); if(u==t) return true; for(i=1;i<=t;i++) if(!level[i]&&cap[u][i]>flow[u][i]) { level[i]=level[u]+1; Q.push(i); } } return false; } int dfs(int u,int maxf,int t){ if(u==t||maxf==0) return maxf; int ret=0,f,i; for(i=1;i<=t;i++) if(cap[u][i]>flow[u][i]&&level[u]+1==level[i]){ f= dfs(i,min(maxf,cap[u][i]-flow[u][i]),t); flow[u][i]+=f; flow[i][u]-=f; maxf-=f; ret+=f; if(maxf==0) break; } return ret; } int Dinic(int s,int t){ int flow=0; while(BFS(s,t)){ flow+=dfs(s,INF,t); } return flow; } void init(){ // memset(cap,0,sizeof(cap)); // memset(flow,0,sizeof(flow)); } int main(){ int i,j,k; while(scanf("%d %d %d",&K,&C,&M)!=EOF){ N=K+C; for(i=1;i<=N;i++) for(j=1;j<=N;j++){ scanf("%d",&dis[i][j]); if(dis[i][j]==0) dis[i][j]=INF; } for(k=1;k<=N;k++) for(i=1;i<=N;i++) for(j=1;j<=N;j++) if(dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[i][k]+dis[k][j]; /* for(i=0;i<=N+1;i++,printf(" ")) for(k=0;k<=N+1;k++) printf("%d ",cap[i][k]);*/ int L=0,R=100000,mid; int tp; while(L<R){// 二分枚举 max_min mid=(L+R)>>1; build(mid); tp=Dinic(0,N+1); if(tp>=C) R=mid; else L=mid+1; } printf("%d ",R); } return 0; }
14 / 31 Problem O POJ 3189 Steady Cow Assignment
二分图多重匹配/网络流
题目描述:有n头奶牛,m个棚,每个奶牛对每个棚都有一个喜爱程度。当然啦,棚子也是有脾气的,并不是奶牛想住进来就住进来,
超出棚子的最大容量了,棚子是拒绝的。现在要给每个奶牛安家,本宝宝是一个公正无私的人类,
所以要找一个奶牛喜爱程度差值最小的方案(虽然这样大家的喜爱程度可能普遍偏低,因为绝对公平并不代表合理啊),问喜爱程度的区间最小为多大?
解题思路:每个棚子并不是住一个奶牛,所以是多重匹配咯。匹配的时候二分枚举喜爱程度的区间大小,根据区间大小来枚举区间的起点和终点,
然后跑多重匹配判断是否合法即可。
数据读入的时候maps[i][j]并不是i_th奶牛对j_th棚子的喜爱值,而是i_th奶牛对maps[i][j]棚子的喜爱程度为j。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 1010; int maps[maxn][22], vis[22], used[22][maxn]; int link[22], limit[22], n, m, s, e, mid; bool Find (int u) { for (int i=1; i<=m; i++) {//多重匹配 if (!vis[i] && maps[u][i]<e && s<=maps[u][i]) { vis[i] = 1; if (link[i] < limit[i]) {//i棚子未满 used[i][link[i] ++] = u; return true; } for (int j=0; j<limit[i]; j++)//i棚子已满,寻找增广路 if (Find(used[i][j])) { used[i][j] = u; return true; } } } return false; } bool hungry () { for (s=1; s<=m+1-mid; s++) {//枚举区间起点与终点 int ans = 0; e = s + mid; memset (link, 0, sizeof(link)); for (int i=1; i<=n; i++) { memset (vis, 0, sizeof(vis)); if (Find (i)) ans ++; } if (ans == n) return true; } return false; } int main () { while (scanf ("%d %d", &n, &m) != EOF) { for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) { scanf ("%d", &mid); maps[i][mid] = j; } for (int i=1; i<=m; i++) scanf ("%d", &limit[i]); int left = 1, right = m, ans = 0; while (left<=right) {//二分枚举区间 mid = (right+left)/2; if (hungry()) { ans = mid; right = mid - 1; } else left = mid + 1; } printf ("%d ", ans); } return 0; }
法2.首先二分图匹配,正常情况下,都是单个单个的点这样匹配、
但是像这道题,右边的点一个可以匹配好几个左边的点。也是用匈牙利算法,不过要做少少修改。
枚举答案的时候,有两种方法:
一个是移动区间的方法。复杂度O(B)。注意,只移动区间右边的时候不用重新匹配,只需要接着上次没匹配完的继续匹配就行了
一个是二分法。复杂度 O(B lgB)
由于数据的问题,这两种方法时间相差无几。在 16ms 和 32ms 之间。
ps:这里是枚举棚子,与上面不同
#include <stdio.h> struct barn { int link[1024], cnt, vis, cap; }; struct cow { int rank[32]; }; struct barn barns[32]; struct cow cows[1024]; int start, end, last_pos; int N, B, tm; int dfs(int c) { int i, j; struct barn *b; for (i = start; i <= end; i++) { b = &barns[cows[c].rank[i]]; if (b->vis == tm) continue; b->vis = tm; if (b->cnt < b->cap) { b->link[b->cnt++] = c; return 1; } for (j = 0; j < b->cap; j++) if (dfs(b->link[j])) { b->link[j] = c; return 1; } } return 0; } inline void init() { int i; for (i = 1; i <= B; i++) barns[i].cnt = 0; last_pos = 1; } inline int match() { int i, j; for (i = last_pos; i <= N; i++) { tm++; if (!dfs(i)) break; } last_pos = i; return i > N; } int main() { int i, j, ans; //freopen("e:\test\in.txt", "r", stdin); scanf("%d%d", &N, &B); for (i = 1; i <= N; i++) for (j = 1; j <= B; j++) scanf("%d", &cows[i].rank[j]); for (i = 1; i <= B; i++) scanf("%d", &barns[i].cap); #if 1 // O(B) ans = B; start = end = 1; while (start <= B && end <= B) { init(); while (end <= B && !match()) end++; if (end - start + 1 < ans) ans = end - start + 1; start++; } #else // O(B*lgB) int l, r, m; l = 1; r = B; while (l <= r) { m = (l + r) / 2; for (start = 1; start <= B - m + 1; start++) { end = start + m - 1; init(); if (match()) break; } if (start == B - m + 2) { // failed l = m + 1; } else r = m - 1; } ans = r + 1; #endif printf("%d ", ans); return 0; }
29 / 56 Problem P HDU 2255 奔小康赚大钱
KM算法 模板题
#include <stdio.h> #include <algorithm> #include <string.h> #include <iostream> using namespace std; /* KM算法 * 复杂度O(nx*nx*ny) * 求最大权匹配 * 若求最小权匹配,可将权值取相反数,结果取相反数 * 点的编号从0开始 */ const int N = 310; const int INF = 0x3f3f3f3f; int nx,ny;//两边的点数 int g[N][N];//二分图描述 int linker[N],lx[N],ly[N];//y中各点匹配状态,x,y中的点标号 int slack[N]; bool visx[N],visy[N]; bool DFS(int x) { visx[x] = true; for(int y = 0; y < ny; y++) { if(visy[y])continue; int tmp = lx[x] + ly[y] - g[x][y]; if(tmp == 0) { visy[y] = true; if(linker[y] == -1 || DFS(linker[y])) { linker[y] = x; return true; } } else if(slack[y] > tmp) slack[y] = tmp; } return false; } int KM() { memset(linker,-1,sizeof(linker)); memset(ly,0,sizeof(ly)); for(int i = 0;i < nx;i++) { lx[i] = -INF; for(int j = 0;j < ny;j++) if(g[i][j] > lx[i]) lx[i] = g[i][j]; } for(int x = 0;x < nx;x++) { for(int i = 0;i < ny;i++) slack[i] = INF; while(true) { memset(visx,false,sizeof(visx)); memset(visy,false,sizeof(visy)); if(DFS(x))break; int d = INF; for(int i = 0;i < ny;i++) if(!visy[i] && d > slack[i]) d = slack[i]; for(int i = 0;i < nx;i++) if(visx[i]) lx[i] -= d; for(int i = 0;i < ny;i++) { if(visy[i])ly[i] += d; else slack[i] -= d; } } } int res = 0; for(int i = 0;i < ny;i++) if(linker[i] != -1) res += g[linker[i]][i]; return res; } //HDU 2255 int main() { int n; while(scanf("%d",&n) == 1) { for(int i = 0;i < n;i++) for(int j = 0;j < n;j++) scanf("%d",&g[i][j]); nx = ny = n; printf("%d ",KM()); } return 0; }
20 / 33 Problem Q HDU 3488 Tour
KM
好像费用流也可以
题意:这题自己YY了下,没想到结论还是对的。题目告诉我们一个有向图,现在问将图中的每一个点都划分到一个环中的最少代价是多少?每条边都有一个代价。
解法:由于要成环,那么将这个图进行拆点,就变成了单向的二分图了,此时一个完备匹配就是一种连线策略,只要保证没有边是和自己相连,就能够满足题目中要求的每个点至少属于一个环。证明也是很简单的。因为我们总可以从一个完备匹配中找出起点,然后再从匹配点作为起点找......
左图可以看做是1,2成环,3,4,5成环。
代码如下:
#include <cstdlib> #include <cstring> #include <cstdio> #include <iostream> #include <algorithm> using namespace std; int N, M; const int INF = 0x3f3f3f3f; int w[205][205]; int lx[205], ly[205]; int sx[205], sy[205]; int match[205], slack[205]; int path(int u) { sx[u] = 1; for (int i = 1; i <= N; ++i) { if (sy[i]) continue; int t = lx[u] + ly[i] - w[u][i]; if (!t) { sy[i] = 1; if (!match[i] || path(match[i])) { match[i] = u; return true; } } else { slack[i] = min(slack[i], t); } } return false; } void KM() { memset(match, 0, sizeof (match)); memset(lx, 0x80, sizeof (lx)); memset(ly, 0, sizeof (ly)); for (int i = 1; i <= N; ++i) { for (int j = 1; j <= N; ++j) { lx[i] = max(lx[i], w[i][j]); } } for (int i = 1; i <= N; ++i) { memset(slack, 0x3f, sizeof (slack)); while (1) { memset(sx, 0, sizeof (sx)); memset(sy, 0, sizeof (sy)); if (path(i)) break; int d = INF; for (int j = 1; j <= N; ++j) { if (!sy[j]) d = min(d, slack[j]); } for (int j = 1; j <= N; ++j) { if (sx[j]) lx[j] -= d; if (sy[j]) ly[j] += d; else slack[j] -= d; } } } int ret = 0; for (int i = 1; i <= N; ++i) { ret += w[match[i]][i]; } printf("%d ", -ret); } int main() { int T, x, y, ct; scanf("%d", &T); while (T--) { scanf("%d %d", &N, &M); memset(w, 0x80, sizeof (w)); for (int i = 1; i <= M; ++i) { scanf("%d %d %d", &x, &y, &ct); w[x][y] = max(w[x][y], -ct); } KM(); } return 0; }
10 / 20 Problem R URAL 1099 Work Scheduling
一般图匹配带花树
模板题!!!
以下是模板:
/* *********************************************** Author :kuangbin Created Time :2013/8/21 22:56:05 File Name :F:2013ACM练习专题学习图论一般图匹配带花树URAL1099.cpp ************************************************ */ #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <time.h> using namespace std; const int MAXN = 250; int N; //点的个数,点的编号从1到N bool Graph[MAXN][MAXN]; int Match[MAXN]; bool InQueue[MAXN],InPath[MAXN],InBlossom[MAXN]; int Head,Tail; int Queue[MAXN]; int Start,Finish; int NewBase; int Father[MAXN],Base[MAXN]; int Count;//匹配数,匹配对数是Count/2 void CreateGraph() { int u,v; memset(Graph,false,sizeof(Graph)); scanf("%d",&N); while(scanf("%d%d",&u,&v) == 2) { Graph[u][v] = Graph[v][u] = true; } } void Push(int u) { Queue[Tail] = u; Tail++; InQueue[u] = true; } int Pop() { int res = Queue[Head]; Head++; return res; } int FindCommonAncestor(int u,int v) { memset(InPath,false,sizeof(InPath)); while(true) { u = Base[u]; InPath[u] = true; if(u == Start) break; u = Father[Match[u]]; } while(true) { v = Base[v]; if(InPath[v])break; v = Father[Match[v]]; } return v; } void ResetTrace(int u) { int v; while(Base[u] != NewBase) { v = Match[u]; InBlossom[Base[u]] = InBlossom[Base[v]] = true; u = Father[v]; if(Base[u] != NewBase) Father[u] = v; } } void BloosomContract(int u,int v) { NewBase = FindCommonAncestor(u,v); memset(InBlossom,false,sizeof(InBlossom)); ResetTrace(u); ResetTrace(v); if(Base[u] != NewBase) Father[u] = v; if(Base[v] != NewBase) Father[v] = u; for(int tu = 1; tu <= N; tu++) if(InBlossom[Base[tu]]) { Base[tu] = NewBase; if(!InQueue[tu]) Push(tu); } } void FindAugmentingPath() { memset(InQueue,false,sizeof(InQueue)); memset(Father,0,sizeof(Father)); for(int i = 1;i <= N;i++) Base[i] = i; Head = Tail = 1; Push(Start); Finish = 0; while(Head < Tail) { int u = Pop(); for(int v = 1; v <= N; v++) if(Graph[u][v] && (Base[u] != Base[v]) && (Match[u] != v)) { if((v == Start) || ((Match[v] > 0) && Father[Match[v]] > 0)) BloosomContract(u,v); else if(Father[v] == 0) { Father[v] = u; if(Match[v] > 0) Push(Match[v]); else { Finish = v; return; } } } } } void AugmentPath() { int u,v,w; u = Finish; while(u > 0) { v = Father[u]; w = Match[v]; Match[v] = u; Match[u] = v; u = w; } } void Edmonds() { memset(Match,0,sizeof(Match)); for(int u = 1; u <= N; u++) if(Match[u] == 0) { Start = u; FindAugmentingPath(); if(Finish > 0)AugmentPath(); } } void PrintMatch() { Count = 0; for(int u = 1; u <= N;u++) if(Match[u] > 0) Count++; printf("%d ",Count); for(int u = 1; u <= N; u++) if(u < Match[u]) printf("%d %d ",u,Match[u]); } int main() { CreateGraph();//建图 Edmonds();//进行匹配 PrintMatch();//输出匹配数和匹配 return 0; }
7 / 22 Problem S HDU 4687 Boke and Tsukkomi
一般图匹配带花树
此题求哪些边在任何一般图极大匹配中都无用,对于任意一条边i,设i的两个端点分别为si,ti,
则任意一个极大匹配中都必然有si或ti至少一个点被匹配,当在图中去掉si,ti两个点时,匹配数会损失一个或两个.
如果损失两个,就说明在极大匹配中这两个点分别连接不同的边,于是边i是无用的
所以总体思路:一般图匹配求出最大匹配数cnt0,分别试着去掉每条边的端点,再次匹配,匹配数如果小于cnt0-1,则这条边无用,记录
/* *********************************************** Author :kuangbin Created Time :2013/8/23 19:28:08 File Name :F:2013ACM练习专题学习图论一般图匹配带花树HDU4687.cpp ************************************************ */ #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <time.h> using namespace std; const int MAXN = 50; int N; //点的个数,点的编号从1到N bool Graph[MAXN][MAXN]; int Match[MAXN]; bool InQueue[MAXN],InPath[MAXN],InBlossom[MAXN]; int Head,Tail; int Queue[MAXN]; int Start,Finish; int NewBase; int Father[MAXN],Base[MAXN]; int Count; void Push(int u) { Queue[Tail] = u; Tail++; InQueue[u] = true; } int Pop() { int res = Queue[Head]; Head++; return res; } int FindCommonAncestor(int u,int v) { memset(InPath,false,sizeof(InPath)); while(true) { u = Base[u]; InPath[u] = true; if(u == Start) break; u = Father[Match[u]]; } while(true) { v = Base[v]; if(InPath[v])break; v = Father[Match[v]]; } return v; } void ResetTrace(int u) { int v; while(Base[u] != NewBase) { v = Match[u]; InBlossom[Base[u]] = InBlossom[Base[v]] = true; u = Father[v]; if(Base[u] != NewBase) Father[u] = v; } } void BloosomContract(int u,int v) { NewBase = FindCommonAncestor(u,v); memset(InBlossom,false,sizeof(InBlossom)); ResetTrace(u); ResetTrace(v); if(Base[u] != NewBase) Father[u] = v; if(Base[v] != NewBase) Father[v] = u; for(int tu = 1; tu <= N; tu++) if(InBlossom[Base[tu]]) { Base[tu] = NewBase; if(!InQueue[tu]) Push(tu); } } void FindAugmentingPath() { memset(InQueue,false,sizeof(InQueue)); memset(Father,0,sizeof(Father)); for(int i = 1;i <= N;i++) Base[i] = i; Head = Tail = 1; Push(Start); Finish = 0; while(Head < Tail) { int u = Pop(); for(int v = 1; v <= N; v++) if(Graph[u][v] && (Base[u] != Base[v]) && (Match[u] != v)) { if((v == Start) || ((Match[v] > 0) && Father[Match[v]] > 0)) BloosomContract(u,v); else if(Father[v] == 0) { Father[v] = u; if(Match[v] > 0) Push(Match[v]); else { Finish = v; return; } } } } } void AugmentPath() { int u,v,w; u = Finish; while(u > 0) { v = Father[u]; w = Match[v]; Match[v] = u; Match[u] = v; u = w; } } void Edmonds() { memset(Match,0,sizeof(Match)); for(int u = 1; u <= N; u++) if(Match[u] == 0) { Start = u; FindAugmentingPath(); if(Finish > 0)AugmentPath(); } } int getMatch() { Edmonds(); Count = 0; for(int u = 1; u <= N;u++) if(Match[u] > 0) Count++; return Count/2; } bool g[MAXN][MAXN]; pair<int,int>p[150]; int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int m; while(scanf("%d%d",&N,&m)==2) { memset(g,false,sizeof(g)); memset(Graph,false,sizeof(Graph)); int u,v; for(int i = 1;i <= m;i++) { scanf("%d%d",&u,&v); p[i] = make_pair(u,v); g[u][v] = true; g[v][u] = true; Graph[u][v] = true; Graph[v][u] = true; } int cnt0 = getMatch(); //cout<<cnt0<<endl; vector<int>ans; for(int i = 1;i <= m;i++) { u = p[i].first; v = p[i].second; memcpy(Graph,g,sizeof(g)); for(int j = 1;j <= N;j++) Graph[j][u] = Graph[u][j] = Graph[j][v] = Graph[v][j] = false; int cnt = getMatch(); //cout<<cnt<<endl; if(cnt < cnt0-1) ans.push_back(i); } int sz = ans.size(); printf("%d ",sz); for(int i = 0;i < sz;i++) { printf("%d",ans[i]); if(i < sz-1)printf(" "); } printf(" "); } return 0; }