P1402 酒店之王

在这里插入图片描述
简化一下题意:给出 n n n对限制 ( a i , b i ) (a_i,b_i) (ai,bi)代表使用第 a i a_i ai个房间与第 b i b_i bi个菜。每个房间和菜只能使用1次,求最多能满足多少个 ( a i , b i ) (a_i,b_i) (ai,bi).
这不就是匹配问题吗.考虑最大流解决该问题,
边属性: ( u , v , c a p ) (u,v,cap) (u,v,cap)
对于每个客人,建边 ( a i , b i , 1 ) (a_i,b_i,1) (ai,bi,1)
对于房间: ( s , a i , 1 ) (s,a_i,1) (s,ai,1),对食物 ( b i , t i , 1 ) (b_i,t_i,1) (bi,ti,1)
s , t s,t s,t的最大流就okk.
然而并不okk,上边建的边回导致一个人重复造成贡献,也就是在一个人同时喜欢多个食物,房间的搭配的情况下,它的最大贡献也只能是1.
解决方法:这是网络流中结点容量的限制,把房间的边 ( s , a i , 1 ) (s,a_i,1) (s,ai,1)拆成两条边
( s , i , 1 ) , ( i , i + a i , 1 ) (s,i,1),(i,i+a_i,1) (s,i,1),(i,i+ai,1)
可惜,这还是踏马的WA了,直接脑淤血,因为这么做之后,人是唯一的,房间是唯一的,而且食物也要唯一,也就是说,拆三次.
显然,我们有待思考这种拆三次的做法,考虑一下把人作为限制条件,也就是人所造成房间与食物的连边直接拆
在这里插入图片描述
大概长这样,这样保证了这三者节点容量最大只能是1.

/*
Stairs upon the temple
I climb and I crawl in
People travel millions of miles just to see it
But they never conquer this way
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 500+5;
const int INF = 1e9+7;
typedef long long ll;
typedef pair<int,int> pii;
#define all(a) (a).begin(), (a).end()
#define pb(a) push_back(a)
struct Edge{
	int from,to,cap,flow;//起点,终点,容量,流量
};
struct Dicnic{
	int n,m,s,t;//点,边,源点,汇点
	vector<Edge> edges;//边集
	vector<int> G[maxn];
	bool vis[maxn];//bfs 使用的
	int d[maxn];//起点到i的距离,就是bfs得到的层次图,下次dfs沿着d走
	int cur[maxn];//当前弧优化使用
	void addEdge(int from,int to,int cap){
		edges.push_back({from,to,cap,0});
		edges.push_back({to,from,0,0});//反向边
		m = edges.size();
		G[from].push_back(m-2);G[to].push_back(m-1);
		//正边的编号都是偶数,反向边的编号都是奇数
	}
	bool bfs(){
		memset(vis,0,sizeof(vis));
		queue<int> Q;Q.push(s);
		d[s]=0;vis[s]=true;
		while(!Q.empty()){
			int x = Q.front();Q.pop();
			for(auto v : G[x]){
				Edge &e = edges[v];//获得边的编号
				if(!vis[e.to]&&e.cap>e.flow){
					//只有未被访问过,而且位于残量网络中的弧才考虑
					vis[e.to]=1;
					d[e.to]=d[x]+1;Q.push(e.to);
				}
			}
		}
		return vis[t];
	}
        int dfs(int x,int a){
		//寻找增广路,a代表目前经过的路中,每个边剩余的流量
		if(x==t||a==0) return a;//到达终点/残量为0
		int flow = 0,f;
		for(int &i = cur[x];i<G[x].size();i++){
			Edge &e = edges[G[x][i]];
			if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0){
				//首先要按层次图走,然后当前增加流量要是增广路中流量最小的的
				e.flow+=f;
				edges[G[x][i]^1].flow-=f;//正边加流量,反边减流量
				flow+=f;a-=f;
				if(a==0) break;
			}
		}
		return flow;
	}
	int Maxflow(int s,int t){
		this->s=s;this->t=t;
		int flow=0;
		while(bfs()){
			memset(cur,0,sizeof(cur));flow+=dfs(s,INF);
		}
		return flow;
	}
}g;
int room[maxn][maxn],food[maxn][maxn];
// i person love room[i][j] or not
int main(){
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n,p,q;
	cin>>n>>p>>q;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=p;j++){
			cin>>room[i][j];
			if(room[i][j]==1) g.addEdge(j,(p+q+i),1);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=q;j++){
			cin>>food[i][j];
			if(food[i][j]) g.addEdge((p+q+n)+i,p+j,1);
		}
	}
	int s = 0, t = p+q+2*n+5;
	for(int i=1;i<=p;i++) g.addEdge(s,i,1);
	for(int i=1;i<=n;i++) g.addEdge((p+q+i),(p+q+n)+i,1);
	for(int i=1;i<=q;i++) g.addEdge((p+i),t,1);
	cout<<g.Maxflow(s,t)<<"\n";
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

minato_yukina

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值