其他分享
首页 > 其他分享> > 2022 Winter Team Contest - 1-D(二分图最大匹配)

2022 Winter Team Contest - 1-D(二分图最大匹配)

作者:互联网

PDF

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

标签:二分,Contest,int,gp,vis,2022,Team,find,match
来源: https://blog.csdn.net/li3124992092/article/details/122548928