1. 程式人生 > 實用技巧 >[NOI2008] 志願者招募 洛谷P3980

[NOI2008] 志願者招募 洛谷P3980

連結

這是一個看上去很貪心的問題,但是很顯然不能夠直接貪心。

我們面對這一類問題一般採用 \(dp\) 或者網路流來進行分析。

這裡限制很多,狀態複雜,因此不適合 dp ,我們不妨採用網路流來描述這樣一個問題。

我們發現,每一天都需要至少 \(a_i\) 個志願者,而每種志願者都恰好會在一段連續的日子進行工作,我們不妨將每一天拆成兩個點用上下界流量來限制每一天的最少志願者數量。

同時,我們的一種志願者恰好會在一段區間內工作,我們不妨使用差分的思想進行限制,讓這種志願者從 \(s\) 進入,\(t\) 回來,滿足流量守恆,同時在進入的時候需要計算費用。

最後我們只需要跑一遍無源匯最小費用可行流即可。

程式碼:

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

template <typename T>
void read(T &x) {
	T f=1;x=0;char s=getchar();
	while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
	while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s^'0');s=getchar();}
	x *= f; 
}

const int MAXN = 1e6 + 5;
const int MAXM = 1e6 + 5;
const int inf = 1e9;

int head[MAXN] , to[MAXM << 1] , nxt[MAXM << 1] , cnt = 1;
int edge[MAXM << 1] , val[MAXM << 1];
void add(int u , int v , int c , int w) {
	nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v;edge[cnt] = c;val[cnt] = w;
	nxt[++cnt] = head[v];head[v] = cnt;to[cnt] = u;edge[cnt] = 0;val[cnt] = -w;
}

int s , t , num , vis[MAXN] , cur[MAXN];
int dis[MAXN]; 

int MinCost , MaxFlow;
bool bfs() {
	for (int i = 1; i <= num; ++i) vis[i] = 0 , dis[i] = inf , cur[i] = head[i];
	dis[s] = 0;
	queue <int> q;q.push(s);
	int flag = 0;
	while(!q.empty()) {
		int x = q.front();
		q.pop();
		vis[x] = 0;
		for (int i = head[x]; i; i = nxt[i]) {
			if(!edge[i]) continue;
			int v = to[i];
			if(dis[v] > dis[x] + val[i]) {
				dis[v] = dis[x] + val[i];
				if(v == t) {
					flag = 1;
					continue;
				}
				if(!vis[v]) q.push(v) , vis[v] = 1;
			}
		}
	}
	return flag;
}

int Dinic(int x , int flow) {
	if(x == t) {
		MaxFlow += flow;
		MinCost += flow * dis[t];
		return flow;
	}
	vis[x] = 1;
	int rest = flow;
	for (int i = cur[x]; i && rest; i = nxt[i]) {
		cur[x] = i;
		if(!edge[i]) continue;
		int v = to[i];
		if(!vis[v] && dis[v] == dis[x] + val[i]) {
			int k = Dinic(v , min(rest , edge[i]));
			if(!k) dis[v] = -inf;
			rest -= k;
			edge[i] -= k;
			edge[i ^ 1] += k;
		}
	}
	return flow - rest;
}

void MVMC() {
	MinCost = MaxFlow = 0;
	while(bfs()) Dinic(s , inf);
}

int n , m;

int main() {
	read(n),read(m);
	num = n * 2 + m + 2;
	s = n * 2 + m + 1 , t = n * 2 + m + 2;
	for (int i = 1; i <= n; ++i) {
		int x;
		read(x);
		add(i , t , x , 0);
		add(s , i + n , x , 0);
		add(i , i + n , inf - x , 0);
		if(i != n) add(i + n , i + 1 , inf , 0);
	}
	for (int i = 1; i <= m; ++i) {
		int st , ti , c;
		read(st),read(ti),read(c);
		add(n * 2 + i , st , inf , c);
		add(ti + n , n * 2 + i , inf , 0);
	}
	MVMC();
	printf("%d\n" , MinCost);
	return 0;
}