二分图最大匹配 匈牙利算法 专题 Acwing 372 373 374

        任意两条边都没有公共端点的边的集合被称为图的一组匹配。在二分图中,包含边数最多的一组匹配被称为二分图的最大匹配

        匈牙利算法(增广路算法)时间复杂度 O(NM)

       能用二分图匹配的模型一般包括两个要素

        0要素 每个集合内部有0条边

        1要素 每个节点只能和一条匹配边相连

372. 棋盘覆盖     

原题链接   

题目大意

有一个N*M的棋盘 某些格子禁止放置 求最多能向棋盘放入多少个长为2 宽为1的骨牌

题目思路

把棋盘变成二分图,把所有的格子根据行与列的和的奇数偶数不同染成黑白两色 黑白两色互相连接对应一个骨牌占两个格子

首先确定两个要素

1要素每个格子只能有一个骨牌放置 ,0要素同色格子之间无法由骨牌连接 然后对于每一个黑色格子判断能否与身边的四个相连 如果超过界限或者禁止放置就不连接,否则连接

最后对于每个黑色的块判断有没有匹配边 如果有ans++,因为如果有n个骨牌 那么一定会在n个黑(白)色块里找到匹配边

#include
using namespace std;
const int maxn=1e2+10;
int n,t;
int mp[maxn][maxn];
struct node{
	int next,to,dis;
}e[maxn*maxn*maxn];
int cnt=0,head[maxn*maxn*10],vis[maxn*maxn],match[10*maxn*maxn];
int turn[4][2]={1,0,0,1,-1,0,0,-1};

void add(int u,int v,int d)
{
	e[++cnt].dis=d;
	e[cnt].to=v;
	e[cnt].next=head[u];
	head[u]=cnt;
}
bool dfs(int x)
{
	for(int i=head[x];i;i=e[i].next)
	{
		int y=e[i].to;
		if(vis[y]==0)
		{
			vis[y]=1;
			if(!match[y]||dfs(match[y]))
			{
				match[y]=x;
				return 1;
			}
		}
	}
	return 0;
}
int main()
{
	cin>>n>>t;
	for(int i=1;i<=t;i++)
	{
		int a,b;
		cin>>a>>b;
		mp[a][b]=1;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(mp[i][j]||(i+j)&1)continue;
			for(int k=0;k<=3;k++)
			{
				int ii=i+turn[k][0];
				int jj=j+turn[k][1];
				if(mp[ii][jj]||ii<=0||ii>n||jj<=0||jj>n)continue;
				add((i-1)*n+j,(ii-1)*n+jj,1);
				//add((ii-1)*n+jj,(i-1)*n+j,1);
			}
		}
	 } 
	 int ans=0;
	 for(int i=1;i<=n;i++)
	 {
	 	for(int j=1;j<=n;j++)
	 	{
		 if((i+j)&1)continue;
	 		if(mp[i][j])continue;
	 		memset(vis,0,sizeof(vis));
	 		if(dfs((i-1)*n+j))ans++;
		}
	 }
	 cout<

373. 車的放置

题目链接

题目大意

与372差不多 但改成了 每个车之间不能相互攻击到

题目思路

题目转化为再每个行和每个列只能放一个车 但是有的地方不能放置 问你最多能放多少个车

将车的行转化为左点集 列转化为右点集合 如果某个点(i,j)不能放置意味着该点所在的行与列不能进行连边,即ai与bj不能连接 否则把该点所在的行与列进行连边 即ai与bj能连

#include
using namespace std;
const int maxn=200+10;
int n,m,t;
int mp[maxn][maxn];
struct node{
	int next,to,dis;
}e[maxn*maxn*maxn];
int cnt=0,head[maxn*maxn*10],vis[maxn*maxn],match[10*maxn*maxn];
int turn[4][2]={1,0,0,1,-1,0,0,-1};

void add(int u,int v,int d)
{
	e[++cnt].dis=d;
	e[cnt].to=v;
	e[cnt].next=head[u];
	head[u]=cnt;
}
bool dfs(int x)
{
	for(int i=head[x];i;i=e[i].next)
	{
		int y=e[i].to;
		if(vis[y]==0)
		{
			vis[y]=1;
			if(!match[y]||dfs(match[y]))
			{
				match[y]=x;
				return 1;
			}
		}
	}
	return 0;
}
int main()
{
	cin>>n>>m>>t;
	for(int i=1;i<=t;i++)
	{
		int a,b;
		cin>>a>>b;
		mp[a][b]=1;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(mp[i][j])continue;
			add(i,j,1);
			//add(j+n,i)
		}
	 } 
	 int ans=0;
	 for(int i=1;i<=n;i++)
	 {
	 	
	 		//if(mp[i][j])continue;
	 		memset(vis,0,sizeof(vis));
	 		if(dfs(i))ans++;
		
	 }
	 cout<

374. 导弹防御塔

题目链接

题目大意

有n个防御塔和m个敌人 每个防御塔可以进行无限次攻击 但是 每个防御塔进行攻击需要t1的准备时间 攻击完之后有t2冷却时间 导弹飞到怪兽地点需要两点的距离dis/速度v 问你如何在最短时间消灭所有怪兽 以上的条件T1 T2 v dis n m 防御塔和敌人的坐标都会给出

题目思路

遇到这种求最短时间之类的问题就很容易想到二分答案

由于每个导弹塔可以发送多发子弹 最多发m发 因此可以判断出这个题其实是一个多重匹配问题

因此我们需要进行拆点 将怪兽作为二部图的左边节点 将导弹塔所可能发出的每发导弹作为右部节点 预处理每个右部节点 预处理出来他们的初始发射时间 和所属于的导弹塔

然后对于check函数 如果这颗导弹的初始发射时间加上路程走的时间要小于等于mid的话就加上这条边 否则不加 然后对于每个左点进行dfs 最后如果ans==m则说明每个怪兽都会被消灭 return1 else return 0

代码

#include
using namespace std;
const int maxn=60;
double eps=1e-7;
int n,m,t;
double t1,t2;
int v,tot;
int mp[maxn][maxn*maxn];
struct node
{
    int x,y;
}a[maxn],b[maxn];
struct new_node{
    int id;double t;
}c[maxn*maxn];
struct xy{
	int next,to,dis;
}e[maxn*maxn*maxn];
int cnt=0,head[maxn*maxn*10],vis[maxn*maxn],match[10*maxn*maxn];
void add(int u,int v,int d)
{
	e[++cnt].dis=d;
	e[cnt].to=v;
	e[cnt].next=head[u];
	head[u]=cnt;
}
bool dfs(int x)
{
	for(int i=head[x];i;i=e[i].next)
	{
		int y=e[i].to;
		if(vis[y]==0)
		{
			vis[y]=1;
			if(!match[y]||dfs(match[y]))
			{
				match[y]=x;
				return 1;
			}
		}
	}
	return 0;
}
double dis(const node &x,const node &y){
    return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));
}
int check(double mid)
{
	memset(match,0,sizeof(match));
	memset(e,0,sizeof(e));
	memset(head,0,sizeof(head));
	cnt=0;
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=tot;j++)
		{
			if(c[j].t+dis(b[c[j].id],a[i])/v<=mid)
			{
				add(i,j,1);
			}
		}
	}
	int ans=0;
	for(int i=1;i<=m;i++)
	{
		memset(vis,0,sizeof(vis));
		if(dfs(i))ans++;
	}
	if(ans==m)return 1;
	return 0;
}
int main()
{
	cin>>n>>m>>t1>>t2>>v;
	tot=n*m;
	t1/=60;
	for(int i=1; i<=m; i++) scanf("%d%d",&a[i].x,&a[i].y);
    for(int i=1; i<=n; i++) 
    {
        scanf("%d%d",&b[i].x,&b[i].y);
    }
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int k=(j-1)*n+i;
			c[k].id=i;
			c[k].t=(j-1)*(t1+t2)+t1;
		}
	} 
//	go(i,1,m)
//        go(j,1,n){
//            k=(i-1)*n+j;
//            c[k].id=j;
//            c[k].t=(i-1)*(t1+t2)+t1;
//        }
	double l=t1,r=(m+1)*(t1+t2)+10000;
	while(r-l>eps)
	{
		double mid=(l+r)/2.0;
		//cout<

你可能感兴趣的:(算法)