POJ 3057 <二分图匹配>

题意是给一个矩阵。

这个矩阵由X和.和D组成。

D表示门,X表示障碍物,每个.上有个人,问全部人最短逃出门的时间,每秒门只能跑一个人。

如果有人不能逃出输出impossible。

从每个人战的位置开始bfs,得到每个门到每个人的距离,如果有一个人到不了任意一个门,那么就说明有人无法逃出。

可以二分这个t,看是否所有人都能逃出。如何看呢,可以建立很多个二元组门和t,意思是比如1、2、3结点代表3个门在1s的时间,

4、5、6代表3个门在2s的时间,然后进行最大匹配(DFS搜索pre不为-1的时候,找pre[u]而不是u),如果全部人都能匹配成功,说明这个时间是可以让所有人跑掉的。

对于这些门的结点如何表示,可以新建立一个vector表明人可以连到的门,对于这个t,只有到门的时间>=t,对于这个门,这个人连接的是从到门的时间到t时间这个区间的结点,

如果这个门本身最近就要8s,而t=4s,显然就不相连

二分的时候门这么写。

for(int i=1;i<=t1;i++){
		for(int j=0;j<q2[i].size();j++){
			for(int k=q2[i][j].cost;k<=t;k++){
					u.num=q2[i][j].num+t2*(k-1);
					u.cost=k;
					q3[i].push_back(u);
			//	cout << i <<"   " <<j<< "   " <<k<< endl;
			}
		}
	}
如果对于每个t每次都清空这个vecotr重新创建显然很浪费时间。

根据最大匹配的性质,其为找增广路径,那么我们可以每次t+1的时候,把所有结点能新到的门连上,保留之前的增广路径,在此基础上再向下找,这样显然省许多时间

2种方法的代码

#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <stdlib.h>
using namespace std;
vector<int>p[150];
struct ttt{
	int r,c,s;
};
struct tt2{
	int num,cost;
};
int t1;
vector<tt2>q2[150];
vector<ttt>qq[150];
vector<tt2>q3[150];
char map1[40][40];
int walked[40][40];
int map2[40][40];
int dr[4]={0,1,0,-1};
int dc[4]={1,0,-1,0};
int bfs(int r,int c,int t1){
	ttt u;
	u.r=r;u.c=c;u.s=0;
	memset(walked,0,sizeof(walked));
	queue<ttt>q1;
	q1.push(u);
	while(!q1.empty()){
		ttt v=q1.front();q1.pop();
		if(map1[v.r][v.c]=='D'){
			tt2 u1;
			u1.num=map2[v.r][v.c];
			u1.cost=v.s;
			q2[t1].push_back(u1); //q2存每个空地连接的图和cost 
			continue;
		}
		u.s=++v.s;
		for(int i=0;i<4;i++){
			u.r=v.r+dr[i];
			u.c=v.c+dc[i];
			if(map1[u.r][u.c]!='X'&&walked[u.r][u.c]==0){
				walked[u.r][u.c]=1;
				q1.push(u);
			}
		}
	}
	if(q2[t1].size()==0)
	return 0;
	else
	return 1;
}
int walk[150];
int pre[150];
int dfs(int x){
	if(walk[x]==0){
		walk[x]=1;
		for(int i=0;i<q3[x].size();i++){
			int u=q3[x][i].num;
			if(pre[u]==-1||dfs(u)){
				pre[u]=x;
				return 1;
			}
		}
	}
	return 0;
}
int t2;
int fen(int t){
	memset(q3,0,sizeof(q3));
	tt2 u;
	for(int i=1;i<=t1;i++){
		for(int j=0;j<q2[i].size();j++){
			for(int k=q2[i][j].cost;k<=t;k++){
					u.num=q2[i][j].num+t2*(k-1);
					u.cost=k;
					q3[i].push_back(u);
			//	cout << i <<"   " <<j<< "   " <<k<< endl;
			}
		}
	}
	int t2=0;
	memset(pre,-1,sizeof(pre));
	for(int i=1;i<=t1;i++){
		memset(walk,0,sizeof(walk));
		if(dfs(i))t2++;
	}
	if(t2==t1)return 1;
	else return 0;
}
int main(){
    freopen("in.txt","r",stdin);
    int t3,f1,f2,f3,i,j,k,l,n,m;
    int k1,k2;
    int g1,g2;
    int r,c;
    memset(map1,0,sizeof(map1));
    cin >> r>> c;
    t1=0;
    t2=0;
    for(i=1;i<=r;i++)
    	for(j=1;j<=c;j++){
		cin >> map1[i][j];
		if(map1[i][j]=='\0'||map1[i][j]=='\n'){
			j--;continue;
		}else if(map1[i][j]=='D'){
			ttt u;
			u.r=i;
			u.c=j;
			qq[t2].push_back(u);
			//cout <<t2 << "门的" <<i << "   " << j<<endl;
			map2[i][j]=++t2; //map2存图的下标,从0开始 
		}
	}
	int ccc=0;
	for(i=2;i<r;i++) //每个空区域都站着一个人 
    	for(j=2;j<c;j++){
    			if(map1[i][j]=='.')
    		if(bfs(i,j,++t1)==0)  //t1从1开始 
			ccc=1;
	}
	if(ccc){
		cout << "impossible" << endl;return 0;
	}
	int left1,right1,mid1;
	left1=0;right1=(r-2)*(c-2)+1;
	t3=1e9+7;
	while(right1>=left1){
		mid1=(right1+left1)/2;
		if(fen(mid1)){
			right1=mid1-1;
			t3=min(t3,mid1);
		}else{
			left1=mid1+1;
		}
	}
	cout << t3<< endl;
    return 0;	
}

t一点点增加

#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <stdlib.h>
using namespace std;
char map1[15][15];
int map2[15][15];
bool walked[15][15];
int pre[120];
int walk[120];
struct ttt{
	int r,c,s;
};
int dr[4]={0,-1,0,1};
int dc[4]={1,0,-1,0};
struct tt2{
	int num,cost;
};
vector<tt2>qq[120];
vector<int>qq1[120];
int cc1; 
int bfs(int r,int c){
	ttt u,v;
	u.r=r;u.c=c;u.s=0;
	queue<ttt>q1;
	q1.push(u);
	cc1++;
//	cout << r<< "  " <<c <<endl;
	memset(walked,0,sizeof(walked));
	while(!q1.empty()){
		v=q1.front();q1.pop(); //这个点到各个门的费用 
		if(map1[v.r][v.c]=='D'){
			tt2 u1;
			u1.cost=v.s;u1.num=map2[v.r][v.c];
			qq[cc1].push_back(u1);
			continue;
		}
		u.s=++v.s;
		for(int i=0;i<4;i++){
			u.r=v.r+dr[i];
			u.c=v.c+dc[i];
			 if(map1[u.r][u.c]!='X'&&walked[u.r][u.c]==0){
				walked[u.r][u.c]=1;
				q1.push(u);
			}
		}
	}
	if(qq[cc1].size()==0)
	return 0;
	else
	return 1;
}
int vis[120];
int dfs(int x){
	if(walk[x]==0){
		walk[x]=1;
		for(int i=0;i<qq1[x].size();i++){
			int u=qq1[x][i];
			if(pre[u]==-1||dfs(pre[u])){
				pre[u]=x;
				vis[x]=1; //说明x已经连有了 
		//	cout << x << "有了" << endl;
				return 1;
			}
		}
	}
	return 0;
}
int main(){
	int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,r,c;
	freopen("in.txt","r",stdin);
	cin >> r>> c;
	f1=0;
	for(i=1;i<=r;i++)
		for(j=1;j<=c;j++){
			cin >> map1[i][j];
			if(map1[i][j]=='\0'||map1[i][j]=='\n'){
				j--;continue;
			}else if(map1[i][j]=='D'){
				map2[i][j]=++f1;
			}
		}
		int ccc=0;
		for(i=2;i<r;i++)
			for(j=2;j<c;j++){
				if(map1[i][j]=='.'){
				if(bfs(i,j)==0)
				ccc=1;
			}
		}
			if(ccc==1){
				cout << "impossible" << endl;return 0;
			}
	t1=0;
	int res=0;
	memset(pre,-1,sizeof(pre));
	memset(vis,0,sizeof(vis));
	while(t1<r*c){
		t1++;
		for(i=1;i<=cc1;i++){
			for(j=0;j<qq[i].size();j++){
				if(qq[i][j].cost<=t1){
					qq1[i].push_back(f1*(t1-1)+qq[i][j].num);
				}
			}
		}
		for(i=1;i<=cc1;i++){
			memset(walk,0,sizeof(walk));
			if(vis[i]==0){
			if(dfs(i)==1){
			res++;}
		}
		}
	//	cout << t1 << "  " <<res<<  "    " << cc1<<endl;
		if(res>=cc1)break;
	}
	cout << t1<< endl;
    return 0;	
}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值