题解 P3529 【[POI2011]PRO-Programming Contest】
最大流
(罚时:每道题被完成的 时间点 之和)
先不看罚时最小的要求,只考虑做题数量最多,可以像这样建图:
1 每个人连向自己会做的题,由于一个人只会做一道题一次,所以容量为1
2 每道题连向汇点,由于一道题只需要被做一次,所以容量为1
3 源点连向每个人,由于每个人最多的做题数为t/r,所以容量填t/r
最大流就是最大做题数
再考虑罚时最小,就是要求从比赛开始,要尽可能更多人在做题,上面这种建图的缺陷在于可能一个人包揽了很多题,导致罚时很大(明明他可以让别人做一些)导致这种为题就在于源点向人连的边容量过大,一开始允许他做很多题。
所以可以动态加边,每次加一组源点到人的容量为1的边,慢慢开放他们做题的权限,防止一个人疯狂做题,直到某一次即使加边他们也做不出更多的题目,目前的流量图就是一个合法的方案。
输出方案:对于每条人到题目正向边,如果被流满则这个人做了这道题。
#include<bits/stdc++.h>
#define N 500000
#define inf 1000000007
using namespace std;
int n,m,r,t,k,first[N],nxt[N],tot=1,S,T;
int in1[N],in2[N],depth[N],maxflow=0,flow,max_num,last=0,ans=0;
int ans1[N],ans2[N],ans3[N],tpp=0,p[N]={0};
queue <int> q;
struct Edge
{
int u,v,flow;
}edge[N];
void Add(int u,int v,int flow)
{
tot++;
nxt[tot]=first[u];
first[u]=tot;
edge[tot]=(Edge){u,v,flow};
return;
}
void Addedge(int u,int v,int flow)
{
Add(u,v,flow);
Add(v,u,0);
return;
}
int bfs()
{
memset(depth,0,sizeof(depth));
while (q.size()) q.pop();
depth[S]=1;
q.push(S);
while (q.size())
{
int tmp=q.front();
q.pop();
for (int j=first[tmp];j!=-1;j=nxt[j])
if (edge[j].flow&&!depth[edge[j].v])
{
int to=edge[j].v;
depth[to]=depth[tmp]+1;
q.push(to);
}
}
if (depth[T]) return 1;
else return 0;
}
int Dinic(int p,int f)
{
if (p==T) return f;
int k,rest=f;
for (int j=first[p];j!=-1&&rest;j=nxt[j])
if (edge[j].flow&&depth[edge[j].v]==depth[p]+1)
{
int to=edge[j].v;
k=Dinic(to,min(rest,edge[j].flow));
if (!k)
{
depth[to]=0;
continue;
}
edge[j].flow-=k;
edge[j^1].flow+=k;
rest-=k;
}
return f-rest;
}
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
memset(first,-1,sizeof(first));
scanf("%d%d%d%d%d",&n,&m,&r,&t,&k);
S=n+m+1;
T=n+m+2;
for (int i=1;i<=k;i++) scanf("%d%d",&in1[i],&in2[i]);
for (int i=1;i<=k;i++) Addedge(in1[i],in2[i]+n,1);
for (int i=n+1;i<=n+m;i++) Addedge(i,T,1);
max_num=t/r;
for (int i=1;i<=max_num;i++)
{
for (int i=1;i<=n;i++) Addedge(S,i,1);
while (bfs())
while ((flow=Dinic(S,inf))) maxflow+=flow;
if (maxflow==last) break;
last=maxflow;
}
for (int i=2;i<=tot;i+=2)
{
int p1=edge[i].u,p2=edge[i].v;
if (edge[i].flow==0&&p1!=S&&p2!=T&&i%2==0)
{
tpp++;
ans1[tpp]=p1;
ans2[tpp]=p2-n;
ans3[tpp]=p[p1]*r;
p[p1]++;
ans+=ans3[tpp]+r;
}
}
printf("%d %d\n",maxflow,ans);
for (int i=1;i<=tpp;i++) printf("%d %d %d\n",ans1[i],ans2[i],ans3[i]);
// for (int i=2;i<=tot;i+=2) printf("%d %d %d\n",edge[i].u,edge[i].v,edge[i].flow);
return 0;
}