Monkeys in the Emei Mountain UVA - 11167
网络流·最大流·离散化·输出方案
很有价值的一道网络流
题目大意:
题目大概说有n只猴子(LJM),猴子们在某个时间段[li,ri]需要喝vi的水,猴子在一个时间单位里能喝一单位的水。各个单位时间段最多允许m只猴子同时喝水,问猴子们能否成功喝水并输出一个可行的方案,输出方案的时间段区间要从小到大排序并且合并连续的区间。
猴子数n<=100,l,r,v<=50000
题解:
非常直观的模型:
S -> monkey i vi
monkey i -> time j 1 (j∈[li,ri])
time j -> T m
但是时间节点很多,会超时,怎么办呢?
猴子很少啊,换言之,有用的时间节点很少。
因此可以把时间离散化,让一个时间节点代表一段时间。
建图如下:
S -> monkey i vi
monkey i -> time j Rj-Lj ([Lj,Rj]包含于[li,ri])
time j -> T m*(Rj-Lj)
跑最大流,检验流量是否是 Σvi
如何寻找一个方案呢?
首先找到monkey x 连向time t 的流量f>0的边,
这说明x在t代表的这个时间内喝了f的水。
令used[t][k]表示时间段t的第k个喝水位置已经用到了used[t][k]的时间点。
我们在给猴子找位置的时候从小到大枚举k,贪心的先用编号小的位置,把前面的尽量填满再用后面的位置。这个根据used计算并且更新used即可。
而且这样保证了一只猴子不会在同一时刻用两个位置。
最后把给猴子选好的时间段排个序,合并一下。
详情见代码。
Code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <algorithm>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
#define MP make_pair
#define fir first
#define sec second
using namespace std;
typedef pair<int,int> pii;
const int N = 1005;
const int M = 2e5+5;
const int INF = 0x3f3f3f3f;
int n,m,S,T,tot;
int tp[N],tpcnt;
int sz[N],used[N][105];
pii ans[N][105];
struct Node{
int l,r,v;
}monkey[N];
struct Edge{
int to,next,flow,cap;
}e[M];
int head[N], ec=1;
void clear(){ memset(head,0,sizeof(head)); ec=1; }
void add(int a,int b,int c){
ec++; e[ec].to=b; e[ec].cap=c; e[ec].flow=0;
e[ec].next=head[a]; head[a]=ec;
}
void add2(int a,int b,int c){
// D(a); D(b); D(c); E;
add(a,b,c); add(b,a,0);
}
bool vis[N]; int d[N];
bool bfs(){
memset(vis,false,sizeof(vis));
queue<int> q; q.push(S); d[S]=0; vis[S]=true;
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(e[i].cap>e[i].flow && !vis[v]){
vis[v]=true; d[v]=d[u]+1;
if(v==T) return true;
q.push(v);
}
}
}
return false;
}
int dfs(int u,int a){
if(u==T || a==0) return a;
int flow=0,f;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(d[v]==d[u]+1 && (f=dfs(v,min(a,e[i].cap-e[i].flow)))){
flow+=f; a-=f;
e[i].flow+=f; e[i^1].flow-=f;
if(a==0) break;
}
}
if(a) d[u]=-1;
return flow;
}
int mxf(){
int flow=0;
while(bfs()){
flow+=dfs(S,INF);
}
return flow;
}
void build(){
clear(); S=0; T=n+tpcnt+1;
for(int i=1;i<=n;i++)
add2(S,i,monkey[i].v);
for(int i=1;i<tpcnt;i++)
add2(i+n,T,m*(tp[i]-tp[i-1]));
for(int i=1;i<=n;i++)
for(int j=1;j<tpcnt;j++)
if(monkey[i].l<=tp[j-1] && monkey[i].r>=tp[j])
add2(i,j+n,tp[j]-tp[j-1]);
}
void work(){
int flow=mxf();
if(flow<tot){
puts("No"); return;
}
puts("Yes");
for(int x=1;x<=n;x++){
// D(x); E;
for(int i=head[x];i;i=e[i].next){
if(!e[i].flow || e[i].cap==0) continue;
int f=e[i].flow;
int t=e[i].to-n-1;
for(int k=1;k<=m;k++){
if(used[t][k]==tp[t+1]-tp[t]) continue;
if(used[t][k]+f<=tp[t+1]-tp[t]){
ans[x][++sz[x]]=MP(tp[t]+used[t][k],tp[t]+used[t][k]+f);
used[t][k]+=f;
break;
}
else{
f-=(tp[t+1]-tp[t]-used[t][k]);
ans[x][++sz[x]]=MP(tp[t]+used[t][k],tp[t+1]);
used[t][k]=tp[t+1]-tp[t];
if(!f) break;
}
}
}
sort(ans[x]+1,ans[x]+1+sz[x]);
// for(int i=1;i<=sz[x];i++){
// printf(" (%d,%d)",ans[x][i].fir,ans[x][i].sec);
// }
// E;
ans[x][sz[x]+1]=MP(-1,-1);
}
for(int x=1;x<=n;x++){
// D(x); E;
int tp=0, a=1,b=1;
while(a<=sz[x]){
while(b<sz[x] && ans[x][b].sec==ans[x][b+1].fir) b++;
// D(a); D(b); E;
ans[x][++tp]=MP(ans[x][a].fir,ans[x][b].sec);
a=b=b+1;
}
sz[x]=tp;
}
for(int x=1;x<=n;x++){
printf("%d",sz[x]);
for(int j=1;j<=sz[x];j++){
printf(" (%d,%d)",ans[x][j].fir,ans[x][j].sec);
}
puts("");
}
}
int main(){
freopen("a.in","r",stdin);
int cas=0;
while(~scanf("%d",&n) && n){
scanf("%d",&m);
tot=0; tpcnt=0;
memset(sz,0,sizeof(sz));
memset(used,0,sizeof(used));
for(int i=1;i<=n;i++){
scanf("%d%d%d",&monkey[i].v,&monkey[i].l,&monkey[i].r);
tp[tpcnt++]=monkey[i].l; tp[tpcnt++]=monkey[i].r;
tot+=monkey[i].v;
}
sort(tp,tp+tpcnt);
tpcnt=unique(tp,tp+tpcnt)-tp;
printf("Case %d: ",++cas);
// for(int i=0;i<tpcnt;i++) D(tp[i]); E;
build();
work();
}
}

本文介绍了一道经典的网络流问题——UVA-11167猴子喝水问题。该问题涉及最大流算法、离散化及输出方案等知识点,并通过实例详细解析了如何建立模型和求解过程。
994

被折叠的 条评论
为什么被折叠?



