额,累死了,,快十天了没更新博客啦,这些天一直在做二分匹配,截止到刚才终于把hdu Index By Type里面的match做完啦。。好吧,一题一题开始说。
http://acm.hdu.edu.cn/showproblem.php?pid=2236
无题II
一般遇到使最大最小差值最小的问题,要想到二分答案,然后枚举下限,对于每一种情况判断是否可行。
因为每行没列都选一个,所以用到二分匹配,行在A中,列在B中,这样每一个数都代表从A到B的一条边。
然后判断在这样的一种情况下,能否找得到一组完备匹配。
1 # include<stdio.h> 2 # include<string.h> 3 # include<stdlib.h> 4 # define N 105 5 int map[N][N],n,used[N]; 6 bool vis[N][N],visit[N]; 7 int s[10005]; 8 bool getnum(int i) 9 { 10 int j; 11 for(j=1;j<=n;j++) 12 { 13 if(vis[i][j] && !visit[j]) 14 { 15 visit[j]=1; 16 if(used[j]==-1 || getnum(used[j])) 17 { 18 used[j]=i; 19 return 1; 20 } 21 } 22 } 23 return 0; 24 } 25 bool Match(int l,int r) 26 { 27 int i,j,count; 28 memset(vis,0,sizeof(vis)); 29 for(i=1;i<=n;i++) 30 { 31 for(j=1;j<=n;j++) 32 { 33 if(map[i][j]>=l && map[i][j]<=r) vis[i][j]=1; 34 } 35 } 36 count=0; 37 memset(used,-1,sizeof(used)); 38 for(i=1;i<=n;i++) 39 { 40 memset(visit,0,sizeof(visit)); 41 if(getnum(i)) count++; 42 } 43 if(count==n) return 1; 44 return 0; 45 } 46 int cmp(const void *a,const void *b) 47 { 48 return *(int *)a - *(int *)b; 49 } 50 int main() 51 { 52 int i,j,ncase,Dnum,k; 53 int right,left,mid,ans; 54 scanf("%d",&ncase); 55 while(ncase--) 56 { 57 scanf("%d",&n); 58 k=0; 59 for(i=1;i<=n;i++) 60 for(j=1;j<=n;j++) 61 { 62 scanf("%d",&map[i][j]); 63 s[++k]=map[i][j]; 64 } 65 qsort(s+1,n*n,sizeof(s[1]),cmp); 66 k=1; 67 for(i=2;i<=n*n;i++) 68 if(s[i]!=s[i-1]) s[++k]=s[i]; 69 Dnum=s[k]-s[1]; 70 left=0;right=Dnum; 71 while(left<=right) 72 { 73 mid=(left+right)/2; 74 for(i=1;s[i]+mid<=s[k];i++) 75 { 76 if(Match(s[i],s[i]+mid)) break; 77 } 78 if( i<=k && s[i]+mid<=s[k]) 79 { 80 ans=mid; 81 right=mid-1; 82 } 83 else left=mid+1; 84 } 85 printf("%d\n",ans); 86 } 87 return 0; 88 }
http://acm.hdu.edu.cn/showproblem.php?pid=1533
Going Home
很裸的一个km,n个人回到n个房子里面,两点之间的权值是他们的曼哈顿距离,让你求 每个人都回到房子里所需走的距离和最小,一般km都是求最大权值和,所以可以把边权取相反数,然后km,之后求得的值再取相反数。
1 # include<stdio.h> 2 # include<string.h> 3 # include<stdlib.h> 4 # define N 105 5 # define INF 0xfffffff 6 int n,k,m; 7 int map[N][N]; 8 struct node{ 9 int x,y; 10 }s1[N],s2[N]; 11 int lx[N],ly[N]; 12 bool visx[N],visy[N]; 13 int slock[N],used[N]; 14 int dis(int i,int j) 15 { 16 int ans,num; 17 num=s1[i].x-s2[j].x; 18 if(num<0) num=-num; 19 ans=s1[i].y-s2[j].y; 20 if(ans<0) ans=-ans; 21 return ans+num; 22 } 23 int Min(int a,int b) 24 { 25 return a<b?a:b; 26 } 27 bool find(int x) 28 { 29 int j,t; 30 visx[x]=1; 31 for(j=1;j<=k;j++) 32 { 33 if(visy[j]) continue; 34 t=lx[x]+ly[j]-map[x][j]; 35 if(t==0) 36 { 37 visy[j]=1; 38 if(used[j]==-1 || find(used[j])) 39 { 40 used[j]=x; 41 return 1; 42 } 43 } 44 else if(t<slock[j]) slock[j]=t; 45 } 46 return 0; 47 } 48 void KM() 49 { 50 int i,j,d; 51 memset(ly,0,sizeof(ly)); 52 memset(used,-1,sizeof(used)); 53 for(i=1;i<=k;i++) 54 { 55 for(j=1;j<=k;j++) 56 slock[j]=INF; 57 for(;;) 58 { 59 memset(visx,0,sizeof(visx)); 60 memset(visy,0,sizeof(visy)); 61 if(find(i)) break; 62 d=INF; 63 for(j=1;j<=k;j++) 64 if(!visy[j]) d=Min(d,slock[j]); 65 66 for(j=1;j<=k;j++) 67 if(visx[j]) lx[j]-=d; 68 69 for(j=1;j<=k;j++) 70 if(visy[j]) ly[j]+=d; 71 else slock[j]-=d; 72 } 73 } 74 } 75 int main() 76 { 77 int i,j,k1,k2,sum; 78 char ch[105]; 79 while(scanf("%d%d",&n,&m)!=EOF) 80 { 81 if(n==0 && m==0) break; 82 k1=k2=0; 83 for(i=0;i<n;i++) 84 { 85 scanf("%s",ch); 86 for(j=0;j<m;j++) 87 { 88 if(ch[j]=='H') 89 { 90 s1[++k1].x=i; 91 s1[k1].y=j; 92 } 93 else if(ch[j]=='m') 94 { 95 s2[++k2].x=i; 96 s2[k2].y=j; 97 } 98 } 99 } 100 k=k1; 101 for(i=1;i<=k;i++) 102 { 103 lx[i]=-INF; 104 for(j=1;j<=k;j++) 105 { 106 map[i][j]=-1*dis(i,j); 107 if(map[i][j]>lx[i]) lx[i]=map[i][j]; 108 } 109 } 110 KM(); 111 sum=0; 112 for(j=1;j<=k;j++) 113 sum+=map[used[j]][j]; 114 printf("%d\n",-sum); 115 } 116 return 0; 117 }
http://acm.hdu.edu.cn/showproblem.php?pid=2255
奔小康赚大钱
还是一个km,直接模板,没什么说的了。
1 # include<stdio.h> 2 # include<string.h> 3 # include<stdlib.h> 4 # define N 305 5 # define INF 0xfffffff 6 int n; 7 int map[N][N]; 8 int lx[N],ly[N]; 9 bool visx[N],visy[N]; 10 int slock[N],used[N]; 11 bool find(int x) 12 { 13 int j,t; 14 visx[x]=1; 15 for(j=1;j<=n;j++) 16 { 17 if(visy[j]) continue; 18 t=lx[x]+ly[j]-map[x][j]; 19 if(t==0) 20 { 21 visy[j]=1; 22 if(used[j]==-1 || find(used[j])) 23 { 24 used[j]=x; 25 return 1; 26 } 27 } 28 else if(t<slock[j]) slock[j]=t; 29 } 30 return 0; 31 } 32 int Min(int a,int b) 33 { 34 return a<b?a:b; 35 } 36 void KM() 37 { 38 int i,j,d; 39 memset(ly,0,sizeof(ly)); 40 memset(used,-1,sizeof(used)); 41 for(i=1;i<=n;i++) 42 { 43 for(j=1;j<=n;j++) slock[j]=INF; 44 for(;;) 45 { 46 memset(visx,0,sizeof(visx)); 47 memset(visy,0,sizeof(visy)); 48 if(find(i)) break; 49 d=INF; 50 for(j=1;j<=n;j++) 51 if(!visy[j]) d=Min(d,slock[j]); 52 for(j=1;j<=n;j++) 53 if(visx[j]) lx[j]-=d; 54 for(j=1;j<=n;j++) 55 if(visy[j]) ly[j]+=d; 56 else slock[j]-=d; 57 } 58 } 59 } 60 int main() 61 { 62 int i,j,sum; 63 while(scanf("%d",&n)!=EOF) 64 { 65 for(i=1;i<=n;i++) 66 { 67 lx[i]=0; 68 for(j=1;j<=n;j++) 69 { 70 scanf("%d",&map[i][j]); 71 if(map[i][j]>lx[i]) lx[i]=map[i][j]; 72 } 73 } 74 KM(); 75 sum=0; 76 for(j=1;j<=n;j++) 77 sum+=map[used[j]][j]; 78 printf("%d\n",sum); 79 } 80 return 0; 81 }
http://acm.hdu.edu.cn/showproblem.php?pid=1083
Courses
有P门课程,N位学生,每一位学生可以代表一门他已经访问过的课程,问最后每一门课是否都有学生代表。
二分匹配,判断最大匹配是否为P。
1 # include<stdio.h> 2 # include<string.h> 3 # include<stdlib.h> 4 # define N 305 5 # define P 105 6 bool map[P][N],vis[N]; 7 int used[N]; 8 int n,p; 9 bool getnum(int i) 10 { 11 int j; 12 for(j=1;j<=n;j++) 13 { 14 if(!vis[j] && map[i][j]) 15 { 16 vis[j]=1; 17 if(used[j]==-1 || getnum(used[j])) 18 { 19 used[j]=i; 20 return 1; 21 } 22 } 23 } 24 return 0; 25 } 26 int main() 27 { 28 int i,j,ncase,num,ans; 29 scanf("%d",&ncase); 30 while(ncase--) 31 { 32 scanf("%d%d",&p,&n); 33 memset(map,0,sizeof(map)); 34 for(i=1;i<=p;i++) 35 { 36 scanf("%d",&num); 37 for(j=1;j<=num;j++) 38 { 39 scanf("%d",&ans); 40 map[i][ans]=1; 41 } 42 } 43 if(p>n) {printf("NO\n");continue;} 44 memset(used,-1,sizeof(used)); 45 for(i=1;i<=p;i++) 46 { 47 memset(vis,0,sizeof(vis)); 48 if(!getnum(i)) break; 49 } 50 if(i==p+1) printf("YES\n"); 51 else printf("NO\n"); 52 } 53 return 0; 54 }
http://acm.hdu.edu.cn/showproblem.php?pid=1281
棋盘游戏
因为车对同一行同一列的相互攻击,所以每一行每一列只能放一个棋子。
求出最大匹配后,因为要判断每一点是否为关键点,枚举最大匹配中的每一点,把他去掉,再求最大匹配数看是否和原来的相等,如果相等,那该点就不是一个关键点,否则是一个关键点。
1 # include<stdio.h> 2 # include<string.h> 3 # include<stdlib.h> 4 # define N 105 5 int n,m,K; 6 bool map[N][N],vis[N]; 7 int used[N],ans[N]; 8 struct node{ 9 int x,y; 10 }s[N*N]; 11 bool getnum(int i) 12 { 13 int j; 14 for(j=1;j<=m;j++) 15 { 16 if(!vis[j] && map[i][j]) 17 { 18 vis[j]=1; 19 if(used[j]==-1 || getnum(used[j])) 20 { 21 used[j]=i; 22 return 1; 23 } 24 } 25 } 26 return 0; 27 } 28 int main() 29 { 30 int i,j,t=0,count,num,a,b,ans1; 31 while(scanf("%d%d%d",&n,&m,&K)!=EOF) 32 { 33 t++; 34 memset(map,0,sizeof(map)); 35 for(i=1;i<=K;i++) 36 { 37 scanf("%d%d",&a,&b); 38 map[a][b]=1; 39 s[i].x=a; 40 s[i].y=b; 41 } 42 memset(used,-1,sizeof(used)); 43 count=0; 44 for(i=1;i<=n;i++) 45 { 46 memset(vis,0,sizeof(vis)); 47 if(getnum(i)) count++; 48 } 49 num=0; 50 for(i=1;i<=K;i++) 51 { 52 a=s[i].x; 53 b=s[i].y; 54 if(used[b]==a) 55 { 56 for(j=1;j<=m;j++) 57 ans[j]=used[j]; 58 map[a][b]=0; 59 memset(used,-1,sizeof(used)); 60 ans1=0; 61 for(j=1;j<=n;j++) 62 { 63 memset(vis,0,sizeof(vis)); 64 if(getnum(j)) ans1++; 65 } 66 if(ans1<count) num++; 67 map[a][b]=1; 68 for(j=1;j<=m;j++) 69 used[j]=ans[j]; 70 } 71 } 72 printf("Board %d have %d important blanks for %d chessmen.\n",t,num,count); 73 } 74 return 0; 75 }
http://acm.hdu.edu.cn/showproblem.php?pid=1498
50 years, 50 colors
扎气球,每一次可以扎破同一行或者同一列相同颜色的气球。对每一种颜色问在扎了k次之后能否把该颜色的气球全扎破。二分匹配,对于每一种颜色,还是把行放在A集合中,列放在B集合中,然后求最小点集覆盖。
最小点集覆盖=最大匹配。
1 # include<stdio.h> 2 # include<string.h> 3 # include<stdlib.h> 4 # define N 105 5 int n,k; 6 bool vis[N],map[N][N]; 7 int used[N],num[55]; 8 struct node{ 9 int x,y; 10 }s[55][10005]; 11 bool getnum(int i) 12 { 13 int j; 14 for(j=1;j<=n;j++) 15 { 16 if(!vis[j] && map[i][j]) 17 { 18 vis[j]=1; 19 if(used[j]==-1 || getnum(used[j])) 20 { 21 used[j]=i; 22 return 1; 23 } 24 } 25 } 26 return 0; 27 } 28 int main() 29 { 30 int i,j,kk,mm[N],ans,a,b,count; 31 while(scanf("%d%d",&n,&k)!=EOF) 32 { 33 if(n==0 && k==0) break; 34 memset(num,0,sizeof(num)); 35 for(i=1;i<=n;i++) 36 { 37 for(j=1;j<=n;j++) 38 { 39 scanf("%d",&ans); 40 num[ans]++; 41 s[ans][num[ans]].x=i; 42 s[ans][num[ans]].y=j; 43 } 44 } 45 kk=0; 46 for(i=1;i<=50;i++) 47 { 48 if(num[i]<=k) continue; 49 memset(map,0,sizeof(map)); 50 for(j=1;j<=num[i];j++) 51 { 52 a=s[i][j].x; 53 b=s[i][j].y; 54 map[a][b]=1; 55 } 56 memset(used,-1,sizeof(used)); 57 count=0; 58 for(j=1;j<=n;j++) 59 { 60 memset(vis,0,sizeof(vis)); 61 if(getnum(j)) count++; 62 } 63 if(count>k) mm[++kk]=i; 64 } 65 if(kk==0) printf("-1\n"); 66 else 67 { 68 printf("%d",mm[1]); 69 for(i=2;i<=kk;i++) 70 printf(" %d",mm[i]); 71 printf("\n"); 72 } 73 } 74 return 0; 75 }
http://acm.hdu.edu.cn/showproblem.php?pid=3605
Escape
二分图的多重匹配,把原来的used数组改成二维的即可。
由于一个vis数组开大了,各种超时。。
1 # include<stdio.h> 2 # include<string.h> 3 # include<stdlib.h> 4 # define N 100005 5 int num[12]; 6 int used[12][N],ans[12]; 7 bool map[N][12],vis[12]; 8 int n,m; 9 bool getnum(int i) 10 { 11 int j,k; 12 for(j=1;j<=m;j++) 13 { 14 if(!vis[j] && map[i][j]) 15 { 16 vis[j]=1; 17 if(num[j]<ans[j]) 18 { 19 num[j]++; 20 used[j][num[j]]=i; 21 return 1; 22 } 23 else 24 { 25 for(k=1;k<=ans[j];k++) 26 { 27 if(getnum(used[j][k])) 28 { 29 used[j][k]=i; 30 return 1; 31 } 32 } 33 } 34 } 35 } 36 return 0; 37 } 38 int main() 39 { 40 int i,j,sum; 41 while(scanf("%d%d",&n,&m)!=EOF) 42 { 43 for(i=1;i<=n;i++) 44 for(j=1;j<=m;j++) 45 scanf("%d",&map[i][j]); 46 sum=0; 47 for(i=1;i<=m;i++) 48 { 49 scanf("%d",&ans[i]); 50 sum+=ans[i]; 51 } 52 if(sum<n) {printf("NO\n");continue;} 53 memset(used,-1,sizeof(used)); 54 memset(num,0,sizeof(num)); 55 for(i=1;i<=n;i++) 56 { 57 memset(vis,0,sizeof(vis)); 58 if(!getnum(i)) break; 59 } 60 if(i<=n) printf("NO\n"); 61 else printf("YES\n"); 62 } 63 return 0; 64 }
http://acm.hdu.edu.cn/showproblem.php?pid=1151
Air Raid
//路径覆盖就是在图中找一些路经,使之覆盖了图中的所有顶点
最小路径覆盖=顶点数-最大匹配
1 # include<stdio.h> 2 # include<string.h> 3 # include<stdlib.h> 4 # define N 125 5 bool map[N][N],vis[N]; 6 int used[N]; 7 int n,m; 8 bool getnum(int u) 9 { 10 int i; 11 for(i=1;i<=n;i++) 12 { 13 if(!vis[i] && map[u][i]) 14 { 15 vis[i]=1; 16 if(used[i]==-1 || getnum(used[i])) 17 { 18 used[i]=u; 19 return 1; 20 } 21 } 22 } 23 return 0; 24 } 25 int main() 26 { 27 int i,ncase,count; 28 int a,b; 29 scanf("%d",&ncase); 30 while(ncase--) 31 { 32 scanf("%d%d",&n,&m); 33 memset(map,0,sizeof(map)); 34 while(m--) 35 { 36 scanf("%d%d",&a,&b); 37 map[a][b]=1; 38 } 39 memset(used,-1,sizeof(used)); 40 count=0; 41 for(i=1;i<=n;i++) 42 { 43 memset(vis,0,sizeof(vis)); 44 if(getnum(i)) count++; 45 } 46 printf("%d\n",n-count); 47 } 48 return 0; 49 }
http://acm.hdu.edu.cn/showproblem.php?pid=1669
Jamie's Contact Groups
二分答案+多重匹配,开始用邻接表写的,一直WA,最后改成邻接矩阵竟然对了,不知道用邻接表为什么会错,很是不解。
1 # include<stdio.h> 2 # include<string.h> 3 # include<stdlib.h> 4 # define N 1005 5 # define M 505 6 int n,m,mid; 7 int used[M][N],Num[M]; 8 bool vis[M],map[N][M]; 9 bool getnum(int u) 10 { 11 int j,v; 12 for(j=0;j<m;j++) 13 { 14 v=j; 15 if(!map[u][v]) continue; 16 if(vis[v]) continue; 17 vis[v]=1; 18 if(Num[v]<mid) 19 { 20 Num[v]++; 21 used[v][Num[v]]=u; 22 return 1; 23 } 24 for(j=1;j<=Num[v];j++) 25 { 26 if(getnum(used[v][j])) 27 { 28 used[v][j]=u; 29 return 1; 30 } 31 } 32 } 33 return 0; 34 } 35 int main() 36 { 37 int i,j; 38 int flag,num,ans; 39 int l,r; 40 char str[20000]; 41 while(scanf("%d%d",&n,&m)!=EOF) 42 { 43 if(n==0 && m==0) break; 44 memset(map,0,sizeof(map)); 45 for(i=0;i<n;i++) 46 { 47 scanf("%s",str); 48 gets(str); 49 flag=0; 50 for(j=0;str[j];j++) 51 { 52 if(str[j]>='0' && str[j]<='9' && flag==0) 53 { 54 num=str[j]-'0'; 55 flag=1; 56 } 57 else if(str[j]>='0' && str[j]<='9') 58 { 59 num*=10; 60 num+=str[j]-'0'; 61 } 62 else if(str[j]==' ') 63 { 64 if(flag==1) map[i][num]=1; 65 flag=0; 66 } 67 } 68 if(flag==1) map[i][num]=1; 69 } 70 l=n/m; 71 if(l*m!=n) l++; 72 r=n; 73 while(l<=r) 74 { 75 mid=(l+r)/2; 76 memset(Num,0,sizeof(Num)); 77 for(i=0;i<n;i++) 78 { 79 memset(vis,0,sizeof(vis)); 80 if(!getnum(i)) break; 81 } 82 if(i==n) {ans=mid;r=mid-1;} 83 else l=mid+1; 84 } 85 printf("%d\n",ans); 86 } 87 return 0; 88 }
http://acm.hdu.edu.cn/showproblem.php?pid=2413
Against Mammoths
我也是用二分答案做的,但是有一组例子都跑不出来。。。
1 1
0 40000 40000 39999
40000
结果应该是1600040000。
难道是我水过了????