BZOJ 1061: [Noi2008]志愿者招募 最小费用最大流
作者:互联网
title
BZOJ 1061
Description
申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。
Input
第一行包含两个整数N, M,表示完成项目的天数和可以招募的志愿者的种类。 接下来的一行中包含N 个非负整数,表示每天至少需要的志愿者人数。 接下来的M 行中每行包含三个整数Si, Ti, Ci,含义如上文所述。为了方便起见,我们可以认为每类志愿者的数量都是无限多的。
Output
仅包含一个整数,表示你所设计的最优方案的总费用。
Sample Input
3 3
2 3 4
1 2 2
2 3 5
3 3 2
Sample Output
14
HINT
1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1。
analysis
这道题是要好好写写题解的。
刚开始我所想到的模型便是 有源汇下界网络流,嗯,不会处理,愣了一个小时。
下午放学来机房,请教了 \(Chdy\),发现建图有个小技巧,真妙!
我原本的思路是把第 \(i\) 天与第 \(i+1\) 天之间连边,但是容量肯定要比第 \(i\) 天所需志愿者数量大的,所以有点难受。
然后,这个小技巧就是:
我们观察到:为了方便起见,我们可以认为每类志愿者的数量都是无限多的。
嗯,那么我们就把所有的边容量设为 \(INF\),为了满足每天至少需要的志愿者数 \(x\),就用 \(INF-x\) 当做他们的容量,强行将下界转化为上界,即我们写的最普通的网络流。
还有一个思维技巧:我们将所有边的容量都归属于他左边的端点,这样我们若是要保证第 \(n\) 天的所需志愿者数,就需要建一个 \(n+1\) 点,但是我们同时也要保证和汇点连的那条边的容量是 \(INF\),所以就需要再建一个 \(汇点t\) ,与 \(n+1\) 连容量为 \(INF\),代价为 \(0\)的边。
然后其他的就没什么必要说了,都是套路。
code
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+10,inf=0x3f3f3f3f;
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
template<typename T>inline void write(T x)
{
if (!x) { putchar('0'); return ; }
if (x<0) putchar('-'), x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) putchar(ch[num--]);
}
int ver[maxn<<1],edge[maxn<<1],Next[maxn<<1],cost[maxn<<1],head[maxn],len=1;
inline void add(int x,int y,int z,int c)
{
ver[++len]=y,edge[len]=z,cost[len]=c,Next[len]=head[x],head[x]=len;
ver[++len]=x,edge[len]=0,cost[len]=-c,Next[len]=head[y],head[y]=len;
}
int s,t;
int dist[maxn],incf[maxn],pre[maxn];
bool vis[maxn];
inline bool spfa()
{
memset(dist,0x3f,sizeof(dist));
memset(vis,0,sizeof(vis));
queue<int>q;q.push(s);
dist[s]=0,vis[s]=1,incf[s]=1<<30;
while (!q.empty())
{
int x=q.front();
q.pop();
vis[x]=0;
for (int i=head[x]; i; i=Next[i])
{
if (!edge[i]) continue;
int y=ver[i];
if (dist[y]>dist[x]+cost[i])
{
dist[y]=dist[x]+cost[i];
incf[y]=min(incf[x],edge[i]);
pre[y]=i;
if (!vis[y]) q.push(y),vis[y]=1;
}
}
}
if (dist[t]==inf) return false;
else return true;
}
long long maxflow,ans;
inline void update()
{
int x=t;
while (x!=s)
{
int i=pre[x];
edge[i]-=incf[t];
edge[i^1]+=incf[t];
x=ver[i^1];
}
maxflow+=incf[t];
ans+=dist[t]*incf[t];
}
int main()
{
int n,m;
read(n);read(m);
s=0,t=n+m+1;
for (int i=1,x; i<=n; ++i) read(x),add(i,i+1,inf-x,0);//将下界网络流转化为上届网络流的一个神奇操作
add(s,1,inf,0),add(n+1,t,inf,0);
for (int i=1,si,ti,ci; i<=m; ++i) read(si),read(ti),read(ci),add(si,ti+1,inf,ci);
while (spfa()) update();
write(ans);
return 0;
}
标签:ch,dist,1061,招募,志愿者,int,incf,BZOJ,Noi2008 来源: https://www.cnblogs.com/G-hsm/p/11329960.html