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