二分图最小点覆盖 POJ1325 Machine Schedule POJ2226 Muddy Fields

二分图最小点覆盖

给定一张二分图,求出一个最小的点集S,使得图中任意一条边都有至少一个端点S,这个问题被称为二分图的最小点覆盖,简称最小覆盖

这种问题的模型特点是 每条边有两个端点,二者至少选择一个 这是2要素。

Konig定理

二分图最小点覆盖包含的点数等于二分图最大匹配包含的边数

上面这个定理的证明这里就不证了 网上可以搜到

这个证明很关键的一点让我们可以把这个最小覆盖问题的求解转化为对于最大匹配的求解

POJ1325 Machine Schedule

原题链接

题目大意

有AB两台机器 一共有N个任务 每台机器有M不同的模式 第i个任务可以在A机器上以a[i] ,在B机器上以b[i]进行。

每次转化模式 会重启 问你如何让重启次数最少

题目思路

让a的m种模式作为左部图 b的m种模式作为右部图 每个任务作为无向边链接

让重启次数最少 = 让机器使用的模式种类最少 = 二部图中的点最少=最小点覆盖问题=最大匹配问题

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn=1e2+10;
int n,m,k;
int a[maxn],b[maxn];
struct node{
	int next,to,dis;
}e[maxn*maxn];
int cnt=0,head[maxn*10],vis[maxn],match[10*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(-1==match[y]||dfs(match[y]))
			{
				match[y]=x;
				return 1;
			}
		}
	}
	return 0;
}
void init()
{
	memset(e,0,sizeof(e));
	memset(match,-1,sizeof(match));
	memset(head,0,sizeof(head));
	cnt=0;
}
int main()
{
	while(cin>>n)
	{
		if(n==0)break;
		cin>>m>>k;
		init();
		for(int i=1;i<=k;i++)
		{
			int x,c,b;
			cin>>x>>b>>c;
			//cout<<x<<" "<<a[x]<<" "<<b[x]<<endl;
			if (!c || !b) continue;
			add(b,c,1);
			//add(a[i],b[i],1);
		}
		int ans=0;
		for(int i=0;i<n;i++)
		{
			memset(vis,0,sizeof(vis));
			if(dfs(i))ans++;
		}
		cout<<ans<<endl;
	}
	return 0;
}

POJ2226 Muddy Fields

题目链接

题目大意

n*m的格子有的格子脏 有的干净 你需要用1*x的板子覆盖这些脏地方 要求 板子长度不限 数量不限不能覆盖干净地方 但是可以重复覆盖脏地方

题目思路

每块(i,j)泥地要么被i行的一块横着的模板盖住 要么被j列的一块竖着的木板盖住 而这至少选择一个 这是题目的2要素

把一些横着的模板作为左部点 竖着的模板作为右部点 如果(i,j)是脏的那么让这块地上的横模板和竖模板链接 求出二分图最小覆盖,就是用最少的木板覆盖所有泥地

 代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
int n,m;
char c[60][60];
int h[60][60],l[60][60];
struct node{
	int next,to,dis;
}e[maxn*maxn];
int cnt=0,head[maxn*10],vis[maxn],match[10*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]==-1||dfs(match[y]))
			{
				match[y]=x;
				return 1;
			}
		}
	}
	return 0;
}
int main()
{
	cin>>n>>m;
	memset(match,-1,sizeof(match));
	for(int i=0;i<n;i++)
	{
		scanf("%s",&c[i]);
		//printf("%s",c[i]+1);
	}
	int cnt1=0;
	for(int i=0;i<n;i++)
	{	
		int flag=0;
		for(int j=0;j<m;j++)
		{
			
			if(c[i][j]=='*'&&flag==0)
			{
				flag=1;
				h[i][j]=++cnt1;
			}
			else if(flag==1)
			{
				if(c[i][j]=='*')
				{
					h[i][j]=cnt1;
				}
				else 
				{
					flag=0;
				}
			}
		}
	}
	int cnt2=0;
	for(int j=0;j<m;j++)
	{	
		int flag=0;
		for(int i=0;i<n;i++)
		{
			
			if(c[i][j]=='*'&&flag==0)
			{
				flag=1;
				l[i][j]=++cnt2;
			}
			else if(flag==1)
			{
				if(c[i][j]=='*')
				{
					l[i][j]=cnt2;
				}
				else 
				{
					flag=0;
				}
			}
		}
	}
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(c[i][j]=='.')continue;
			//cout<<h[i][j]<<endl;
			add(h[i][j],l[i][j],1);
		}
	}
	int ans=0;
	//cout<<cnt<<endl;
	for(int i=1;i<=cnt1;i++)
	{
		memset(vis,0,sizeof(vis));
		if(dfs(i))ans++;
	}
	cout<<ans<<endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值