2020牛客暑期多校训练营(第一场)

I 1 or 2

题意:一个图有n个点,m条边,边的表示方法是用n个点中的两个。这n个点都有一个要求,就是需要有 d i di di 条边经过自己。然后给你这n个点的要求,还有m条野生的边,让你任意选若干条,满足每一个点的要求,可以的话输出Yes,不行就No。

思路: 注:并不是正解。 这个样例过不了

3 3
1 1 1
1 2
2 3
3 1

这个题目最后一句好难读。读懂之后觉得是个最大流问题,奈何刷的太少,图建不出来。一开始这么想:每个点都有一个需求量,我们可以搞一个超级源点来发射这些需求,然后用一个超级汇点来接收流过来的最大需求量,然后比较一下,源点发出的和汇点接收的一样的话就是ok的,不一样就证明需求量有损失,边的个数是不够的。有这个思路就可以 建图 之后用 最大流 来解决。

以第三个样例说明一下

我一开始建的图:

image-20200712195716348

随便来了一👋把这个图建出来之后感觉妙极了,根本不晓得完全错了。这个图只能筛选掉边数不够的情况,因为这个图会把所有的边都接上来,如果有的节点经过的边不能满足需求,那么从他那里出来的边的个数就不够需求量,到达汇点的量自然就不够数,所以这个图没有考虑到那种边给的超级多,超出了你的需求量的那种,我这个流过去也是可以前后相等的,所以不行。

那有个什么办法可以限制一👋这种超过需求量的情况呢?可以用和源点相同的办法,你源点发出的需求量是根据每个点分配的,那么我汇点接收也要落实到每个点。这个时候就要用一下最大流的常用手段(应该是的吧),把一个点分成两个用,分割的点就用来连接汇点和之前跟自己连接的原来本身的点,这样每一个点的流量就可以框起来了。文字有一点抽象,上图:

中间的流量都是1。

image-20200712200954261

图都建好了,这个题也就是上模版了。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
struct EDGE {
	int to, w, next;
} edge[2000];
int cnt;
int head[2000];
int deep[2000];

void init() {
	cnt = 0;
	memset(head, -1, sizeof(head));
}

void addedge(int u, int v, int w) {
	edge[cnt].to=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++;
	edge[cnt].to=u; edge[cnt].w=0; edge[cnt].next=head[v]; head[v]=cnt++;
}

int bfs(int s, int t) {
	queue<int> q;
	memset(deep, 0, sizeof(deep));
	q.push(s);
	deep[s] = 1;
	int top;
	while(!q.empty()) {
		top = q.front();
		q.pop();
		for(int i = head[top];i+1; i = edge[i].next) {
			int v = edge[i].to;
			int w = edge[i].w;
			if(!deep[v]&&w) {
				deep[v] = deep[top]+1;
				q.push(v);
			}
		}
	}
	return deep[t];
}

int dfs(int s, int t, int inflow) {
	if(s==t) return inflow;
	int acflow=0;
	for(int i=head[s]; i+1; i=edge[i].next) {
		if(edge[i].w && deep[s]+1 == deep[edge[i].to]) {
			int x = dfs(edge[i].to,t,min(inflow, edge[i].w));
			if(x>0) {
				acflow+=x; inflow-=x;edge[i].w -= x; edge[i^1].w += x;
				if(!inflow) break;
			}
		}
	}
	if(!acflow) deep[s] = -2;
	return acflow;
}

int dinic(int s,int t) {
	int f = 0;
	while(bfs(s,t)) f += dfs(s,t,INF);
	return f;
}

int n, m, a, b;
int d[55];

int main() {
	while(~scanf("%d %d", &n, &m)) {
		init();
		int sum = 0;
		for(int i = 1;i <= n; i++) {
			scanf("%d", &d[i]);
			sum += d[i];
			addedge(0, i, d[i]);
		}
		
		int l = 1e9, r = 0;
		for(int i = 1;i <= m; i++) {
			scanf("%d %d", &a, &b);
			addedge(a, b+n, 1);
			l = min(l, b+n); r = max(r, b+n);
			addedge(b, a+n, 1);
			l = min(l, a+n); r = max(r, a+n);
		}
		
		int fin = r+1;
		for(int i = l;i <= r; i++) {
			addedge(i, fin, d[i-n]);
		}
		
		printf("%s\n", dinic(0, fin)==sum?"Yes":"No");
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值