D - Swap Free
大意:
给你n个字符串,长度相等,字符串之间没有重复,单个字符串内的字符也不重复,可以对字符串执行一种操作,操作就是任意选两个字符交换位置,如果无法将集合中的任何单词转换为集合中的任何其他单词,则一组单词称为无交换,让你求最大无交换集
可以把他们每个字符串看成图的点,他们之间的(无交换)关系看作边, 集合中任意两个字符串都符合这个关系就可以看成一个完全图,所以我们要找这个最大的完全图
一个图的最大完全图等于它的补图的最大独立集
最大独立集=顶点数-最小点覆盖
这个图的点需要经过偶数次交换才能回到自身,所以是个二分图
二分图的最小点覆盖等于=最大匹配数
所以问题就转化为求图的最大匹配问题
#include <iostream> #include <bits/stdc++.h> using namespace std; int n; string str[555]; vector<int>ma[555]; int vis[555]; int match[555]; int ans; bool find(int x) { ///找最大匹配 int len=ma[x].size(); for(int i=0; i<len; i++) { if(!vis[ma[x][i]]) { vis[ma[x][i]] = 1; if(!match[ma[x][i]]||find(match[ma[x][i]])) { match[ma[x][i]] = x; return 1; } } } return 0; } int main() { scanf("%d",&n); for(int i=1; i<=n; i++) { cin>>str[i]; } int len=str[1].length(); for(int i=1; i<=n; i++) { for(int j=i+1; j<=n; j++) { int num=0; for(int k=0; k<=len; k++) { if(str[i][k]!=str[j][k]) num++; if(num>2) break; } if(num==2) { ///只有两个交换 ma[i].push_back(j); ma[j].push_back(i); } } } ///二分图的最大独立集等于图中点的个数 - 最大匹配数。 ans=0; for(int i=1; i<=n; i++) { memset(vis,0,sizeof(vis)); if(find(i)) ans++; } cout<<n-ans/2<<endl; return 0; }
来个小小变形题
Asteroids
大意:给你一个n*n的矩阵,图上给你k个‘X’,有个炸弹能一次炸掉一行或一列,问你最少炸几次能炸完.
把行坐标,列坐标,抽象成二分图的左半边,右半边,行列坐标的交点如果有’x’就连成一条边,想要把’x’炸完,就是把边去掉,也就是问你最少删除几个顶点,能把所有的边删除,解决这种问题就是最小点覆盖问题,又因为行和行之间不可能有边,同理列也不可能有边,所以这个图也是个二分图,由二分图的最小点覆盖等于=最大匹配数可知,这个题就是让求最大匹配数的.
#include<string.h> #include<iostream> using namespace std; const int N=555,M=N*N; int n,k; bool gp[N][N]; bool vis[N]; int match[N]; bool find(int u){ for(int i=1;i<=n;i++){ if(!vis[i]&&gp[u][i]){ vis[i]=1; if(!match[i]||find(match[i])){ match[i]=u; return 1; } } } return 0; } int main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin>>n>>k; int a,b; for(int i=1;i<=k;i++){ cin>>a>>b; gp[a][b]=1; } int ans=0; for(int i=1;i<=n;i++){ memset(vis,0,sizeof vis); ans+=find(i); } cout<<ans; }
感觉不够?那就再来个
A - Fire Net
虽然是vj上的,但是题源在航电那··· ···交不了了
大意:给你一个n*n的矩阵,矩阵由’.‘和’X’组成,’.‘可以放大炮,可以往上下左右发射炮弹,这个’X’是墙,炮弹打不透这个墙,问你最多可以放多少门炮在’.'上,让他们互相打不到;
思路:
首先处理这个烦人的墙:
只看行,某一行在有墙阻挡的时候,能放多个炮台
这个墙就把行分成了多个可以放一个炮台的’子行’
同理,列一样
现在这些新的子行,子列就可以任意配对,转化为八皇后问题了
然后用匈牙利算法,把行当作二分图的左边的点,列当作二分图的右边的点
行和列的最大交点数,也就是二分图的最大匹配数就算出来了
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=100,M=N*N; int n,ans; int nu,nv; int newgp[N][N]; int match[N]; bool vis[N]; char gp[N][N]; struct node { int u,v; } a[N][N]; bool find(int u) { for(int i=1;i<=nv;i++){ if(!vis[i]&&newgp[u][i]){ vis[i]=1; if(match[i]==0||find(match[i])){ match[i]=u; return 1; } } } return 0; } void init() { ans=0; nu=nv=1; memset(match,0,sizeof match); memset(a,0,sizeof a); memset(newgp,0,sizeof newgp); memset(vis,0,sizeof vis); } int main() { ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); while(cin>>n,n) { init(); for(int i=1; i<=n; i++) { cin>>gp[i]+1; } for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { if(gp[i][j]=='.') { if(j==1||gp[i][j-1]=='X') nu++; a[i][j].u=nu; } if(gp[j][i]=='.') { if(i==1||gp[j-1][i]=='X') nv++; a[j][i].v=nu; } } } for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { if(gp[i][j]=='.') { int u=a[i][j].u; int v=a[i][j].v; newgp[u][v]=1; } } } for(int i=1;i<=nu;i++){ memset(vis,0,sizeof vis); if(find(i)) ans++; } cout<<ans<<endl; } return 0; }