#4740. 校运会

题目描述

S S SS Z Z ZX 的校运会又开始了。

高一 X X X 班的同学们组成了一个由 n n n 名同学构成的代表队。运动会一共设 m m m 个运动项目,每名同学只能报名最多一个项目,每个项目每个班级也只能报名最多一名同学。

高一 X X X 班的体育委员收集了代表队所有名同学的期望参赛项目和预估参赛成绩。预估参赛成绩分为 K K K 档,从第 1 1 1 名到第 K K K 名。同学们只愿意参加上报的比赛项目。

体育委员把收集的数据交给了你,你需要提供一个报名方案,使得在满足比赛规则和同学要求的情况下,使获得第一名的选手最多。如果两个方案获得第一名的选手一样多,则希望获得第二名的选手最多。以此类推,直到第 K K K名。

题解

考虑对一个方案附上权值,方便比较大小,这样的话我们设计1001进制即可。然后上个费用流即可,这里不要求达到最大流!

代码
#include <bits/stdc++.h>
#define I __int128
using namespace std;
const int N=2005,M=30005;
int n,m,k,V[M],hd[N],S,T,W[M],nx[M],t=1,lst[N],pre[N],fl[N],mc[M],s[15];
I vl[15],C[M],d[N];
bool vis[N];
queue<int>q;
void add(int u,int v,int w,I c){
	nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=w;C[t]=c;
}
void ins(int u,int v,int w,I c){
	add(u,v,w,c);add(v,u,0,-c);
}
bool spfa(){
	for (int i=1;i<=T;i++) d[i]=-vl[0],vis[i]=0;
	for (q.push(S),vis[S]=1;!q.empty();){
		int u=q.front();q.pop();vis[u]=0;
		for (int v,i=hd[u];i;i=nx[i]){
			v=V[i];
			if (W[i] && d[v]<d[u]+C[i]){
				d[v]=d[u]+C[i];
				fl[v]=min(fl[u],W[i]);
				pre[v]=u;lst[v]=i;
				if (!vis[v]) q.push(v),vis[v]=1;
			}
		}
	}
	return d[T]!=-vl[0];
}
int main(){
	cin>>n>>m>>k;vl[k]=1;T=n+m+1;fl[0]=1e9;
	for (int i=k-1;~i;i--) vl[i]=vl[i+1]*(min(n,m)+1);
	for (int v,i=1;i<=n;i++){
		scanf("%d",&v);
		for (int x,y;v--;)
			scanf("%d%d",&x,&y),
			ins(i,x+n,1,vl[y]),mc[t^1]=y;
	}
	int v=t;
	for (int i=1;i<=n;i++) ins(S,i,1,0);
	for (int i=1;i<=m;i++) ins(i+n,T,1,0);
	while(spfa()){
		int x=T;
        if (d[T]<0) break;
		while(x!=S){
			W[lst[x]]-=fl[T];
			W[lst[x]^1]+=fl[T];
			x=pre[x];
		}
	}
	for (int i=2;i<=v;i+=2)
		if (!W[i]) s[mc[i]]++;
	for (int i=1;i<=k;i++)
		printf("%d",s[i]),putchar(i<k?' ':'\n');
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值