二分图判定+二分图最大匹配

判定
模板

邻接表

#include<bits/stdc++.h>
using namespace std;
int n,m;
vector<int> node[205];       //一个神奇的模拟邻接表的“超方便”的东西 
int colour[205];



bool dfs(int a,int c)
{
	colour[a]=c;
	for(int i=0;i<node[a].size();i++)
	{
		if(colour[node[a][i]]==c) return 0;
		if(colour[node[a][i]]==0&&!dfs(node[a][i],-c)) return 0;
	}
	return 1;
}


int main()
{
	memset(colour,0,sizeof(colour));
	cin>>n>>m;
	for(int i=1;i<=m;i++) node[i].clear();
	for(int i=1;i<=m;i++)
	{
		int u,v;
		cin>>u>>v;
		node[u].push_back(v);
		node[v].push_back(u);
	}
	for(int i=0;i<n;i++)
	{
		if(colour[i]==0)
		{
			if(!dfs(i,1))
			{
				cout<<"NO"<<endl;
				return 0;
			}
		}
	}
	cout<<"YES"<<endl;
	return 0;
}

邻接矩阵

#include<bits/stdc++.h>
using namespace std;
int n,m;
int edge[205][205];
int colour[205];



bool dfs(int a,int c)
{
	colour[a]=c;
	for(int i=0;i<n;i++)
	{
		if(edge[a][i]==1)
		{
			if(colour[i]==c) return 0;
			if(colour[i]==0&&!dfs(i,-c)) return 0;
		}
	}
	return 1;
}
int main()
{
	memset(edge,0,sizeof(edge));
	memset(colour,0,sizeof(colour));
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		cin>>u>>v;
		edge[u][v]=1;
		edge[v][u]=1;
	}
	for(int i=0;i<n;i++)
	{
		if(colour[i]==0)
		{
			if(!dfs(i,1))
			{
				cout<<"NO"<<endl;
				return 0;
			}
		}
	}
	cout<<"YES"<<endl;
	return 0;
}

例题
题目描述

S S城现有两座监狱,一共关押着N N名罪犯,编号分别为1-N1−N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为cc 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为c c的冲突事件。

每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到S 城Z 市长那里。公务繁忙的Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。

在详细考察了NN 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。

那么,应如何分配罪犯,才能使Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?

输入输出格式

输入格式:
每行中两个数之间用一个空格隔开。第一行为两个正整数N,MN,M,分别表示罪犯的数目以及存在仇恨的罪犯对数。接下来的MM行每行为三个正整数a_j,b_j,c_ja
j
​ ,b
j
​ ,c
j
​ ,表示a_ja
j
​ 号和b_j b
j
​ 号罪犯之间存在仇恨,其怨气值为c_jc
j
​ 。数据保证1<aj≤bj≤N ,0 < cj≤ 1,000,000,0001<aj≤bj≤N,0<cj≤1,000,000,000,且每对罪犯组合只出现一次。

输出格式:
共11 行,为ZZ 市长看到的那个冲突事件的影响力。如果本年内监狱中未发生任何冲突事件,请输出00。

输入输出样例

输入样例#1: 复制
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
输出样例#1: 复制
3512
说明

【输入输出样例说明】罪犯之间的怨气值如下面左图所示,右图所示为罪犯的分配方法,市长看到的冲突事件影响力是35123512(由22 号和33 号罪犯引发)。其他任何分法都不会比这个分法更优。

【数据范围】

对于30%30%的数据有N≤ 15N≤15。

对于70%70%的数据有N≤ 2000,M≤ 50000N≤2000,M≤50000。

对于100%100%的数据有N≤ 20000,M≤ 100000N≤20000,M≤100000。

贪心+并查集

#include<bits/stdc++.h>
using namespace std;
int n,m;
int father[20010],fight[20010]; 
struct abc
{
	int from,to,w;
}edge[100010];
bool cmp(abc a,abc b)
{
	return a.w>b.w;
}
int getfather(int a)
{
	if(father[a]==a) return a;
	return getfather(father[a]);
}
void unionn(int a,int b)
{
	int fa=getfather(a);
	int fb=getfather(b);
	if(fa!=fb) father[fa]=fb;
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>edge[i].from>>edge[i].to>>edge[i].w;
	}
	for(int i=1;i<=n;i++) father[i]=i;
	sort(edge+1,edge+1+m,cmp);
	for(int i=1;i<=m+1;i++)
	{
		int u=edge[i].from;
		int v=edge[i].to;
		if(getfather(u)==getfather(v))
		{
			cout<<edge[i].w;
			return 0;
		}
		if(!fight[u]) fight[u]=v;
		else
		{
			unionn(fight[u],v);
		}
		if(!fight[v]) fight[v]=u;
		else
		{
			unionn(fight[v],u);
		}
	}
	return 0;
}

二分判定+二分答案

#include<bits/stdc++.h>
using namespace std;
int n,m;
bool flag;
vector<int> node[20010];
int colour[20010];
struct abc
{
	int from,to,w;
}edge[100010];
bool cmp(abc a,abc b)
{
	return a.w<b.w;
}
void dfs(int a,int c)
{
	colour[a]=c;
	for(int i=0;i<node[a].size();i++)
	{
		int v=node[a][i];
		if(!colour[v])
		{
			dfs(v,-c);
		}
		else
		{
			if(colour[v]==c) flag=0;
	    }
	}
}
bool check(int pos)
{
	for(int i=1;i<=n;i++) node[i].clear();
	for(int i=pos+1;i<=m;i++)
	{
		node[edge[i].from].push_back(edge[i].to);
		node[edge[i].to].push_back(edge[i].from);
	}
	
	memset(colour,0,sizeof(colour));
	flag=true;
	for(int i=1;i<=n;i++)
	{
		if(!colour[i])
		{
		    dfs(i,1);
			if(!flag) return 0;
		}
	}
	return 1;
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>edge[i].from>>edge[i].to>>edge[i].w;
	}
	sort(edge+1,edge+1+m,cmp);
	int mid,l=0,r=m;
	while(l<r)
	{
		mid=(l+r)/2;
		if(check(mid)==1) r=mid;
		else l=mid+1;
	}
	if(l==1) cout<<"0";
	else cout<<edge[r].w;
	return 0;
}

二分图最大匹配

过山车
#include<bits/stdc++.h>
using namespace std;
int m,n,k,x,y;
vector e[505];
int match[505],used[505];

bool dfs(int x)
{
for(int i=0;i<e[x].size();i++)
{
if(!used[e[x][i]])
{
used[e[x][i]]=1;
if(match[e[x][i]]==0||dfs(match[e[x][i]]))
{
match[e[x][i]]=x;
return 1;
}
}
}
return 0;
}

int main()
{
cin>>m>>n>>k;
for(int i=1;i<=k;i++)
{
cin>>x>>y;
e[x].push_back(y);
}
memset(match,0,sizeof(match));
int sum=0;
for(int i=1;i<=m;i++)
{
memset(used,0,sizeof(used));
if(dfs(i)) sum++;
}
cout<<sum;
return 0;
}

地鼠
描述

在避免了犬类的威胁后,地鼠家族必须面对一个新的捕食者。

这是n个地鼠洞和m个地鼠洞,每个洞在不同的(x, y)坐标上。
一只鹰来了,如果一只地鼠在几秒钟内没有到达一个洞,它很容易被吃掉。
一个洞最多只能救一只地鼠。
所有的地鼠都以同样的速度奔跑v.地鼠家族需要一种能使易受攻击的地鼠数量最小化的逃跑策略。
输入

输入包含几种情况。
每种情况的第一行包含四个小于100的正整数:n、m、s和v。接下来的n行给出了地鼠的坐标;
下面m行给出了地鼠洞的坐标。
所有距离均以米为单位;
所有的时间都是以秒为单位的;
所有速度都是米每秒。
输出

每种情况的输出由一行组成,给出了易受攻击的地鼠的数量。
样例输入

2,2,5 10
1.0 - 1.0
2.0 - 2.0
100.0 - 100.0
20.0 - 20.0
样例输出

1

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<string>
#include<cstring>
#include<ctime>
#include<map>
#include<queue>
#include<iomanip>
#include<cctype>
using namespace std;

double n,m,s,v,sum=0;
struct abc{
	double x,y;
}gop[100005];
abc hol[100005];
int edge[1005][1005];
int match[100005],used[100005];


bool dfs(int x)
{
	for(int i=1;i<=m;i++)
	{
		if(edge[x][i]==1)
		{
			if(!used[i])
			{
				used[i]=1;
				if(match[i]==0||dfs(match[i]))
				{
					match[i]=x;
					return 1;
				}
			}
		}
	}
	return 0;
}

int main()
{
	while(cin>>n>>m>>s>>v)
	{
		memset(edge,0,sizeof(edge));
		for(int i=1;i<=n;i++)
		{
			cin>>gop[i].x>>gop[i].y;
		}
		for(int i=1;i<=m;i++)
		{
			cin>>hol[i].x>>hol[i].y;
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				double w=sqrt((gop[i].x-hol[j].x)*(gop[i].x-hol[j].x)+(gop[i].y-hol[j].y)*(gop[i].y-hol[j].y));
	    		if((w/v)<=s) edge[i][j]=1;
			}
		}
		memset(match,0,sizeof(match));
		sum=0;
		for(int i=1;i<=n;i++)
		{
			memset(used,0,sizeof(used));
			if(dfs(i)) sum++;
		}
		cout<<n-sum<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值