6.3.1 HDU1054 Strategic Game
给出一颗树,如果一个点被覆盖,则与他相连的边都被覆盖,求最少需要多少个点来覆盖这颗树,二分图和树形DP都可以解决,DP会更快一些
DP算法,对于每个点可以选择放或者不放,令all[v]表示覆盖以v为父节点的子树所需要的最小卫兵数,dr[v]表示这一点放卫兵覆盖子树的最小卫兵数,显然对于某一点,若这一点不放,则它的儿子节点都要放sum1=sigma[dr[v]],若这一点放,下面的节点只要被覆盖即可dr[root]=1+sigma(all[v]),最后这个节点的all[root]=min(sum1,dr[root])
二分图算法,最小点覆盖问题,因为边加了两遍,最后要除2,这里贴一下DP算法
#include <cstdio> #include <vector> #include <string.h> using namespace std; vector<int> m[1502]; int n,dr[1502],all[1502]; void dfs(int root,int father){ dr[root]=1; int sum=0; for(int i=0;i<m[root].size();i++){ int v=m[root][i]; if(v!=father){ dfs(v,root); dr[root]+=all[v];//在这一点放,只要下面的点被覆盖即可 sum+=dr[v];//在这一点不放,下一点必须放 } } all[root]=min(sum,dr[root]); } int main(){ while(scanf("%d",&n)!=EOF){ int u,ns,v; for(int i=0;i<n;i++)m[i].clear(); for(int i=0;i<n;i++){ scanf("%d:(%d)",&u,&ns); for(int i=0;i<ns;i++){ scanf("%d",&v); m[u].push_back(v); m[v].push_back(u); } } dfs(0,0); printf("%d\n",min(dr[0],all[0])); } return 0; }
6.3.2 HDU1068 Girls and Boys
因为是无向图,最大独立集=点-最大匹配/2
6.3.3 HDU1150 Machine Schedule
裸二分图,求最大匹配
6.3.4 HDU1151 Air Raid
裸二分图,最小路径覆盖=点-最大匹配
6.3.5 HDU1498 50 years, 50 colors
选择一种颜色,每次冲击可以消去这一行以及列的该颜色的气球,求在K次冲击以内不能消去的颜色。这题主要考察建图,对每种颜色都要建图计算,分别以行和列为二分图的两个部分,如果交点处的气球是该颜色的,则连接这两个点。
#include <cstdio> #include <string.h> #include <algorithm> #include <map> #define MAXN 105 using namespace std; int mar[MAXN][MAXN],nmap[MAXN][MAXN],match[MAXN],vis[MAXN],n,k; int color[MAXN/2],res[MAXN/2],ress,cols;//储存不同颜色,以及答案 map<int,int> mp;//判断颜色是否出现过 int dfs(int p){ for(int i=1;i<=n;i++){ if(nmap[p][i]&&!vis[i]){ vis[i]=1; if(match[i]==-1||dfs(match[i])){ match[i]=p; return 1; } } } return 0; } int solve(){ memset(match,-1,sizeof match); int r=0; for(int i=1;i<=n;i++){ memset(vis,0,sizeof vis); r+=dfs(i); } return r; } int main(){ while(scanf("%d%d",&n,&k),n||k){ mp.clear();ress=0,cols=0; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ scanf("%d",&mar[i][j]); if(mp.count(mar[i][j])==0){ mp[mar[i][j]]=1; color[cols++]=mar[i][j]; } } } //保证按顺序输出 sort(color,color+cols); //对每种颜色建图,找最小点覆盖 for(int i=0;i<cols;i++){ memset(nmap,0,sizeof nmap); for(int i1=1;i1<=n;i1++){ for(int i2=1;i2<=n;i2++){ if(mar[i1][i2]==color[i])nmap[i1][i2]=1; } } int r=solve(); if(r>k)res[ress++]=color[i]; } if(ress==0)printf("-1\n"); else{ printf("%d",res[0]); for(int i=1;i<ress;i++)printf(" %d",res[i]); printf("\n"); } } }
6.3.6 HDU1528 Card Game Cheater
先将牌号转换成相应的分数(自定规则,能有序就行),然后对scb[i]>sca[j]的进行建图即可,求最大匹配
6.3.7 HDU1507 Uncle Tom's Inherited Land*
求图中最多的1*2矩形,显然是二分匹配,建图上要注意,为免重复,行列和为奇数的放左边,偶数放右边,相连的空白方块连线,然后求最大匹配,根据MATCH数组输出结果即可
#include <cstdio> #include <string.h> #include <cmath> using namespace std; struct pos{ int x,y; pos(){}; pos(int a,int b){x=a,y=b;} bool neib(pos p){ if(x==p.x&&(y-p.y==1||p.y-y==1))return true; if(y==p.y&&(x-p.x==1||p.x-x==1))return true; return false; } }evep[100],oddp[100]; int eves,odds,anss,ans[100]; int n,m,k,gra[105][105],x,y; int match[105],vis[105],map[105][105]; int dfs(int p){ for(int i=0;i<odds;i++){ if(map[p][i]&&!vis[i]){ vis[i]=1; if(match[i]==-1||dfs(match[i])){ match[i]=p; return 1; } } } return 0; } int solve(){ int r=0; anss=0; memset(match,-1,sizeof match); for(int i=0;i<eves;i++){ memset(vis,0,sizeof vis); if(dfs(i)){ r++; } } return r; } int main(){ while(scanf("%d%d",&n,&m),n||m){ scanf("%d",&k); memset(gra,0,sizeof gra); for(int i=0;i<k;i++){ scanf("%d%d",&x,&y); gra[x][y]=1; } eves=odds=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(gra[i][j])continue; if((i+j)&1){ evep[eves++]=pos(i,j); }else{ oddp[odds++]=pos(i,j); } } } memset(map,0,sizeof map); for(int i=0;i<eves;i++){ for(int j=0;j<odds;j++){ if(evep[i].neib(oddp[j]))map[i][j]=1; } } printf("%d\n",solve()); for(int i=0;i<eves;i++){ int t=match[i]; if(t==-1)continue; printf("(%d,%d)--(%d,%d)\n",evep[i].x,evep[i].y,oddp[t].x,oddp[t].y); } printf("\n"); } }
6.3.8 HDU2768 Cat vs. Dog
用lc[][0]表示爱猫者喜欢的猫,lc[][1]表示爱猫者不喜欢的狗,ld[][0],ld[][1]同理
分别将爱猫者和爱狗者作为二分图的两边,如果lc[i][0]==ld[j][1]||lc[i][1]==ld[j][0],即两者冲突,就建边,然后求最大独立集
#include <cstdio> #include <string.h> #define MAXN 605 using namespace std; int cas,c,d,v,cs,ds; int map[MAXN][MAXN],vis[MAXN],match[MAXN],lc[MAXN][2],ld[MAXN][2]; char s1[50],s2[50]; int dfs(int p){ for(int i=0;i<ds;i++){ if(map[p][i]&&!vis[i]){ vis[i]=1; if(match[i]==-1||dfs(match[i])){ match[i]=p;return 1; } } } return 0; } int solve(){ int r=0; memset(match,-1,sizeof match); for(int i=0;i<cs;i++){ memset(vis,0,sizeof vis); r+=dfs(i); } return r; } int main(){ scanf("%d",&cas); while(cas--){ scanf("%d%d%d",&c,&d,&v); cs=0,ds=0; char op1,op2;int bh1,bh2; for(int i=0;i<v;i++){ scanf("%s%s",s1,s2); sscanf(s1,"%c%d",&op1,&bh1); sscanf(s2,"%c%d",&op2,&bh2); if(op1=='C'){ lc[cs][0]=bh1;//喜欢的 lc[cs][1]=bh2;//不喜欢的 cs++; }else{ ld[ds][0]=bh1;//喜欢的 ld[ds][1]=bh2;//不喜欢的 ds++; } } memset(map,0,sizeof map); for(int i=0;i<cs;i++){ for(int j=0;j<ds;j++){ //两者有冲突,建一条边 if(lc[i][0]==ld[j][1]||lc[i][1]==ld[j][0])map[i][j]=1; } } //求最大独立集 printf("%d\n",v-solve()); } return 0; }