现在的位置: 首页 > 综合 > 正文

HDU_Steps6.3 二分图 HDU1054 HDU1068 HDU1150 HDU1151 HDU1498 HDU1528 HDU1507 HDU2768

2012年09月01日 ⁄ 综合 ⁄ 共 4565字 ⁄ 字号 评论关闭

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;	
}

 

抱歉!评论已关闭.