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