Programming Contest
POI2011
题意
1.n个人,m个题目
2.比赛有t分钟,每个题目需要r分钟做
3.问做最多题且罚时最少的情况下,最多做了几题,此时最少罚时为多少
解
1.每个题目最多做一次
2.每个人最多做 ⌊ t r ⌋ \lfloor\frac{t}{r}\rfloor ⌊rt⌋ 题
3.那么把每个人拆成
⌊
t
r
⌋
\lfloor\frac{t}{r}\rfloor
⌊rt⌋个人x,x+n,x+n*2,...
相应的费用 r*1,r*2,r*3,...
4.这样相当于每个人做一题
5.二分图匹配
每次把匹配成功的点放进队列,以备下次再次使用
一层一层来,每层的费用都多r
一直匹配,知道比赛时间结束,或者题目被做完为止
具体代码
#include<bits/stdc++.h>
using namespace std;
const int M=505;
int n,m,R,T,K;
int du[M],mat[M],que[2][M],len[2];
int head[M],asdf;
struct edge {
int to,nxt;
} G[M*M];
void add_edge(int a,int b) {
G[++asdf].to=b;
G[asdf].nxt=head[a];
head[a]=asdf;
}
bool vis[M];
bool dfs(int x) {
int c=x%n;
if(c==0)c=n;
for(int i=head[c]; i; i=G[i].nxt) {
int y=G[i].to;
if(vis[y])continue;
vis[y]=1;
if(mat[y]==0||dfs(mat[y])) {
mat[y]=x;
return 1;
}
}
return 0;
}
int main() {
int a,b;
scanf("%d %d %d %d %d",&n,&m,&R,&T,&K);
for(int i=1; i<=K; i++) {
scanf("%d %d",&a,&b);
add_edge(a,b);
du[a]++;
}
int mx=0;
len[0]=0;
for(int i=1; i<=n; i++) {
mx=max(mx,du[i]);
if(du[i])que[0][++len[0]]=i;
}
int goal=min(T/R,mx),sum=0,cost=R;
int ans=0,cnt=0;
int now=0;
while(goal&&cnt<m) {
len[1-now]=0;
for(int i=1; i<=len[now]; i++) {
memset(vis,0,sizeof(vis));
int x=que[now][i]+sum;
if(dfs(x)) {
cnt++;
ans+=cost;
que[1-now][++len[1-now]]=x;
}
}
now=1-now;
cost+=R;
sum+=n;
goal--;
}
printf("%d %d\n",cnt,ans);
return 0;
}