$S$S$Z$X 的校运会又开始了。
高一 $X$ 班的同学们组成了一个由 $n$ 名同学构成的代表队。运动会一共设 $m$ 个运动项目,每名同学只能报名最多一个项目,每个项目每个班级也只能报名最多一名同学。
高一 $X$ 班的体育委员收集了代表队所有名同学的期望参赛项目和预估参赛成绩。预估参赛成绩分为 $K$ 档,从第 $1$ 名到第 $K$ 名。同学们只愿意参加上报的比赛项目。
体育委员把收集的数据交给了你,你需要提供一个报名方案,使得在满足比赛规则和同学要求的情况下,使获得第一名的选手最多。如果两个方案获得第一名的选手一样多,则希望获得第二名的选手最多。以此类推,直到第$K$名。
Sol
有意思的题
题目求一个权值和最大的匹配,其中权值$ v(rk_1) > v(rk_2) \times n$
那么我们令$ v(rk_i)=(n+1)^{k-i} $ ,然后费用流。
注意不一定要满流,$v<0$ 即可退出。
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll __int128
#define maxn 2005
#define inf p[k]
using namespace std;
int n,m,k,head[maxn],tot=1,S,T;
int pre[maxn],flag[maxn];
ll d[maxn],p[15],ans;
struct node{
int v,nex,cap;ll w;
}e[40005];
void add(int t1,int t2,int t3,ll t4){
e[++tot].v=t2;e[tot].cap=t3;e[tot].w=t4;e[tot].nex=head[t1];head[t1]=tot;
e[++tot].v=t1;e[tot].cap=0;e[tot].w=-t4;e[tot].nex=head[t2];head[t2]=tot;
}
bool spfa(){
for(int i=1;i<=T;i++)d[i]=-inf;
queue<int>q;q.push(S);flag[S]=1;d[S]=0;
while(!q.empty()){
int k=q.front();q.pop();
for(int i=head[k];i;i=e[i].nex){
if(e[i].cap&&d[e[i].v]<d[k]+e[i].w){
d[e[i].v]=d[k]+e[i].w;pre[e[i].v]=i;
if(!flag[e[i].v])q.push(e[i].v),flag[e[i].v]=1;
}
}
flag[k]=0;
}
if(d[T]<0)return 0;
ll tmp=0;
for(int i=pre[T];i;i=pre[e[i^1].v]){
e[i].cap--;e[i^1].cap++;tmp+=e[i].w;
}
if(tmp<0)return 0;
ans+=tmp;return 1;
}
int main(){
cin>>n>>m>>k;S=n+m+1,T=S+1;
p[0]=1;for(int i=1;i<=k;i++)p[i]=p[i-1]*(n+1);
for(int i=1,num;i<=n;i++){
scanf("%d",&num);
for(int j=1,x,rk;j<=num;j++){
scanf("%d%d",&x,&rk);rk=k-rk;
add(i,x+n,1,p[rk]);
}
}
for(int i=1;i<=n;i++)add(S,i,1,0);
for(int i=1;i<=m;i++)add(i+n,T,1,0);
while(spfa());
for(int i=k-1;i>=0;i--){
int Sum=0;
while(ans>=p[i])ans-=p[i],Sum++;
printf("%d%c",Sum,i?' ':'\n');
}
return 0;
}