洛谷P1264 k-联赛

在这里插入图片描述
由题目可知若想要一个队伍能够拿到冠军
首先就是先让这个队伍拿到后续比赛所有的分
然后将其他的比赛平均分给 其他队伍 并且他们的分不能大于这个队伍
所以我们只需要将剩余比赛匹配给剩余队伍就行
一开始我还想着去贪心 贪心贪着发现 还是得二分匹配去匹配最优
那这个问题完全可以转换为 二分匹配去匹配最优解 如果全都匹配成功了那么这个队伍就可以成为冠军
匈牙利的复杂度大概是n^3 点的个数大概为 3000 边大概为3000 所以 超时
所以必须使用hk算法 或者 最大流
最大流的话我们只需要将每次比赛当成一个点 源点连向这个比赛 这个比赛连向两个人
两个人连向汇点 即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define INF 0x3f3f3f3f

using namespace std;

const int N = 30,M = N * 110 * 2;

int n,m,S,T;
int a[N],b[N],gam[N][N],out[N];

int head[3000],to[M],w[M],last[M],cnt;
void add(int a,int b,int c){
	to[++cnt] = b;
	w[cnt] = c;
	last[cnt] = head[a];
	head[a] = cnt;
	to[++cnt] = a;
	w[cnt] = 0;
	last[cnt] = head[b];
	head[b] = cnt;
}

int d[3000],cur[3000];
bool bfs(){
	memset(d,0,sizeof d);
	d[S] = 1;
	queue<int>q;
	q.push(S);
	cur[S] = head[S];
	while(q.size()){
		int p = q.front();
		q.pop();
		for(int i = head[p]; i != -1; i = last[i]){
			int j = to[i];
			if(!d[j] && w[i]){
				d[j] = d[p] + 1;
				cur[j] = head[j];
				q.push(j);
				if(j == T) return true;
			}
		}
	}
	return false;
}

int dfs(int x,int sum){
	if(x == T) return sum;
	int used = 0;
	for(int i = cur[x]; i != -1; i = last[i]){
		cur[x] = i;
		int j = to[i];
		if(d[j] == d[x] + 1 && w[i]){
			int dd = dfs(j,min(sum - used,w[i]));
			w[i] -= dd;
			w[i ^ 1] += dd;
			used += dd;
		}
	}
	if(used == 0) d[x] = -1;
	return used;
}

int total;
bool dinic(){
	int sum = 0;
	while(bfs()){
		sum += dfs(S,INF);
	}
	return sum == total;
}

int main(){
	cin >> n;
	for(int i = 1; i <= n; i++){
		scanf("%d%d",&a[i],&b[i]);
	}
	
	S = 0,T = 100 * 25 + n + 1;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= n; j++){
			scanf("%d",&gam[i][j]);
		} 
	}
	
	int idx = 0;
	for(int i = 1; i <= n; i++){
		memset(head,-1,sizeof head);
		
		cnt = 1,total = 0;
		int maxn = 0,sum = 0;
		for(int j = 1; j <= n; j++){
			if(i != j) maxn = max(maxn,a[j]);
			sum += gam[i][j];
		}
		if(a[i] + sum < maxn) continue;
		
		int ans = 0;
		for(int j = 1; j <= n; j++){
			if(i == j) continue;
			for(int k = j + 1; k <= n; k++){
				if(k == i) continue;
				total += gam[j][k];
				add(S,++ans,gam[j][k]);
				add(ans,100 * 25 + j,INF);
				add(ans,100 * 25 + k,INF);
			}
			add(100 * 25 + j,T,a[i] + sum - a[j]);
		}
		
		if(dinic()){
			out[++idx] = i;
		}
	}
	
	for(int i = 1; i <= idx; i++){
		cout << out[i] << ' ';
	}
	
	
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值