巴塞罗那又输球了,在我眼里或许它是完美的,只能希望煤老板早日康复了吧。。
最大团:子图(包括自己)中最大的完全图。
补图:将图补成完全图,缺少的边以及所有的点组成的图。
最小覆盖集:对于每条边至少一个点被选中。
最大独立集就是其补图的最大团。
二分图的最大独立集:集合中任意两个点都没有边相连。
最小路径覆盖: DAG的最小路径覆盖是指找最小数目的互相不相交的有向路径,满足DAG的所有顶点都被覆盖。
通俗的讲就是从起点开始,只经过每个点一次,最后回到这个点。
http://www.cnblogs.com/pushing-my-way/archive/2012/08/08/2627993.html
此处讲解的十分详细。
代码还是要自己注释一下的。
要注意这求的是有环无向图的最大团。
#include <iostream> #include <memory.h> #include <stdio.h> using namespace std; const int maxnum=101; bool array[maxnum][maxnum]; bool use[maxnum]; //进入团的标号 int cn,bestn,p,e; void dfs(int i) { int j; bool flag; if(i>p) { bestn=cn; printf("%d\n",bestn); for(j=1;j<=p;j++) if(use[j]) printf("%d ",j); printf("\n"); return ; } flag=true; for(j=1;j<i;j++) if(use[j]&&!array[j][i]) { flag=false; break; } //判断与相邻的点是否有连接 if(flag) { cn++; use[i]=true; dfs(i+1); cn--; } if(cn+p-i>bestn) //剩下未搜索的点加团中的点少于现在的答案 { use[i]=false; dfs(i+1); } } int main() { int num,i,u,v; scanf("%d",&num); while(num--) { memset(array,false,sizeof(array)); memset(use,false,sizeof(use)); scanf("%d%d",&p,&e); for(i=0;i<e;i++) { scanf("%d%d",&u,&v); array[u][v]=true; array[v][u]=true; } cn=bestn=0; dfs(1); //printf("%d\n",bestn); } return 0; }
这题要注意的是有可能有环存在,所以不可以用二分图的最大独立集=点数-最大匹配数来解决。(至于怎么解决后文会有提到)
1.1求二分图的最小覆盖集与最大独立集。
公式:最大独立集=点数-最大匹配数
最大匹配数=最小覆盖集
LA3415
题意:男女两种人,选中的男女要求满足四种条件。男女人数最大多少。
解法:将不满足条件的男女之间连一条线,最后就变成了求二分图的最大独立集。
代码由于与求最大匹配几乎相同,除了建图有一些不同以外,就不贴了。
1.2求具体最小覆盖集的点
标记右边未匹配边的顶点,并从右边未匹配边的顶点出发,按照边:未匹配->匹配->未匹配...,的原则标记途中经过的顶点,则最后一条经过的边必定为匹配边。重复上述过程,直到右边不再含有未匹配边的点。
记得到的左边已标记的点和右边未标记的点为S, S即为所求的最小顶点集。
uva11419
直接上代码
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 1111; bool w[MAXN][MAXN],vis_x[MAXN],vis_y[MAXN]; int s[MAXN],t[MAXN],left[MAXN]; int match(int i,int c) { s[i] = 1; for(int j = 1;j <= c;j++) if(w[i][j] && !t[j]) { t[j] = 1; if(!left[j] || match(left[j],c)) { left[j] = i; return 1; } } return 0; } void solve(int r,int c) { memset(left,0,sizeof(left)); memset(vis_x,0,sizeof(vis_x)); memset(vis_y,0,sizeof(vis_y)); int ans = 0; for(int i = 1;i <= r;i++) for(int j = 1;j <= c;j++) if(w[i][j] && !vis_x[i] && !vis_y[j]) { vis_x[i] = vis_y[j] = 1; left[j] = i; ans++; } for(int i = 1;i <= r;i++) { memset(s,0,sizeof(s)); memset(t,0,sizeof(t)); if(!vis_x[i] && match(i,c)) ans++; } printf("%d",ans); } int right[MAXN]; void print(int r,int c) { memset(s,0,sizeof(s)); memset(t,0,sizeof(t)); memset(right,0,sizeof(right)); for(int i = 1;i <= c;i++) if(left[i]) { right[left[i]] = 1; } for(int i = 1;i <= r;i++) if(!right[i]) { match(i,c); } for(int i = 1;i <= r;i++) if(!s[i]) printf(" r%d",i); for(int i = 1;i <= c;i++) if(t[i]) printf(" c%d",i); puts(""); } int main() { int r,c,n; while(~scanf("%d%d%d",&r,&c,&n) && r) { memset(w,0,sizeof(w)); while(n--) { int a,b; scanf("%d%d",&a,&b); w[a][b] = 1; } solve(r,c); print(r,c); } return 0; }2.1 DAG最短路径覆盖
最小路径覆盖=N-最大匹配
LA 3126
题意:有 n 个顾客 , 需要坐出租车从一个地方去另一个地方 , 每个顾客的出发时间、出发地点、目的地点都已给出 , 从出发地点到目的地点的时间为两地之间的路径长度 , 并且出租车要比顾客的出发时间早一分钟到达 , 问最少需要派出多少辆出租车。
解法:我们先这样来构图 , 每个顾客是一个结点,如果同一个出租车在接完客人 u 之后还来得及节客人 v , 那么就在 u 到 v 之间连一条有向边 。 由此可以发现 , 这个图是一个DAG , 那么我们就只需要找最小路径覆盖(最小路径覆盖:是指在图中找尽量少的路径,去覆盖每一个点,并且每个路径之间没有公共点)。
对于最小路径覆盖,我们先进行拆点 , 把图边为一个二分图 , 把所有结点 拆为两个结点 , 如 结点 u 拆为 u1 , u2 , 如果 u 到 v 有一条有向边 , 那么我们就连接 u1 到 v2 的一条边。
这时我们只需要求出这个图的最大匹配 , 结果就是: n - m(最大匹配数)。
为什么?
因为匹配和路径覆盖是一一对应的 , 对于路径覆盖中的每条简单路径 , 除了最后一个“结尾结点”之外,都有唯一的后继与其对应(也就是匹配结点) , 因此匹配数就是非“结尾结点”的个数 , 但匹配数达到最大时 , 也就是“结尾结点”最小 , 也就是路径最小。
代码:
#include <iostream> #include <stdio.h> #include <string.h> #include <vector> #include <cmath> using namespace std; #define maxn 520 vector<int>grap[maxn]; int cab[maxn][4] , cab_t[maxn][2]; int n; int pre[maxn] , cx[maxn] , cy[maxn]; void init() { for(int i = 0; i < n; i++) grap[i].clear(); memset(cx , -1 , sizeof(cx)); memset(cy , -1 , sizeof(cy)); } void create_grap() { int i , j; int x , y; int h , m; for(i = 0; i < n; i++) { y = abs(cab[i][0]-cab[i][2])+abs(cab[i][1]-cab[i][3]); for(j = 0; j < n; j++) { if(i == j) continue; x = y+abs(cab[j][0]-cab[i][2])+abs(cab[j][1]-cab[i][3]); m = (x+cab_t[i][1])%60; h = cab_t[i][0]+(x+cab_t[i][1])/60; if(h < cab_t[j][0] || (h == cab_t[j][0]&&m < cab_t[j][1])) grap[i].push_back(j); } } } int findpath(int u) { int i , v; for(i = 0; i < grap[u].size(); i++) { v = grap[u][i]; if(!pre[v]) { pre[v] = 1; if(cy[v] == -1 || findpath(cy[v])) { cy[v] = u; cx[u] = v; return 1; } } } return 0; } int match() { int i , cas = 0; for(i = 0; i < n; i++) if(cx[i] == -1) { memset(pre , 0 , sizeof(pre)); cas += findpath(i); } return cas; } int main() { int t; scanf("%d" , &t); while(t--) { scanf("%d" , &n); init(); int i ; for(i = 0; i < n; i++) scanf("%d:%d %d %d %d %d" , &cab_t[i][0] , &cab_t[i][1] , &cab[i][0] , &cab[i][1] , &cab[i][2] , &cab[i][3]); create_grap(); int cas = match(); cout<<n-cas<<endl; } return 0; }估计二分图还要来一波专题,未完待续……