题解 P3980 【[NOI2008]志愿者招募】
题面
惊喜
又一次考试网络流爆零......
这一题一看就是网络流, 但是要怎么构图呢? 考虑到途中的一些因素, 首先, 每一种志愿者控制的区间范围为
那么初始的构图也就出来了, 每个时间作为一条边, 那么由已经付钱了, 你得给我做完事才能走)(
最终构图是这样的: 每个点
具体代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#define N 2005
#define INF 1e9
using namespace std;
int n, m, S, T, delta[N], head[N], cnt = 1, p[N], vis[N];
struct node
{
int from, to, next;
long long flow, cost;
} edge[100005];
long long a[N], d[N];
inline int read()
{
int x = 0, w = 1;
char c = getchar();
while(c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * w;
}
inline void add(int u, int v, long long w, long long cost)
{
edge[++cnt] = { u, v, head[u], w, cost }; head[u] = cnt;
edge[++cnt] = { v, u, head[v], 0, -cost }; head[v] = cnt;
}
bool SPFA(long long &cost)
{
memset(d, 0x3f, sizeof(d)); memset(a, 0x3f, sizeof(a));
queue<int> q; q.push(S); d[S] = 0; vis[S] = 1;
while(!q.empty())
{
int u = q.front(); q.pop(); vis[u] = 0;
for(int i = head[u]; i; i = edge[i].next)
{
int v = edge[i].to;
if(d[v] > d[u] + edge[i].cost && edge[i].flow > 0)
{
d[v] = d[u] + edge[i].cost; a[v] = min(a[u], edge[i].flow);
p[v] = i; if(!vis[v]) { vis[v] = 1; q.push(v); }
}
}
}
if(d[T] == d[0]) return 0;
cost += (a[T] * d[T]);
for(int i = T; i != S; i = edge[p[i]].from)
{
edge[p[i]].flow -= a[T]; edge[p[i] ^ 1].flow += a[T];
}
return 1;
}
int main()
{
n = read(); m = read();
S = n + 2; T = S + 1;
for(int i = 1; i <= n; i++) { int x = read(); add(i, i + 1, INF, 0); delta[i] -= x; delta[i + 1] += x; }
for(int i = 1; i <= m; i++)
{
int u = read(), v = read(), c = read();
add(v + 1, u, INF, c);
}
for(int i = 1; i <= n + 1; i++)
{
if(delta[i] < 0) add(i, T, -delta[i], 0);
if(delta[i] > 0) add(S, i, delta[i], 0);
}
long long cost = 0;
while(SPFA(cost));
printf("%lld\n", cost);
return 0;
}