NOI2008 誌願者招募
阿新 • • 發佈:2019-04-22
reg size spfa def node 發現 getch line cpp ,容量為\(inf\),費用為\(c_i\)。
題目傳送門
Description
一個項目分\(n\)天完成,每天需要\(a_i\)個誌願者。誌願者有\(m\)種招募方式,第\(i\)種從第\(s_i\)天工作到第\(t_i\)天,工資\(c_i\)元。求完成項目的最少花費。\((n\leq 10^3,m\leq 10^4)\)
Solution
數據範圍和題目描述都指向網絡流,且是最小費用最大流。
但如何建圖?這就不顯然了。
以每天為一個節點,並建立源點和匯點。
源點連第一天,最後一天連匯點,容量為\(inf\),費用為\(0\)。
每一天連後一天,容量為\(inf-a_i\),費用為\(0\)。
對於每一類誌願者,\(s_i\)連\(t_i\)
然後跑最小費用最大流即可。
為什麽這樣建圖可行?不難發現,只要題目有解,最大流一定為\(inf\),也就是用有費用的流將所有的\(a_i\)都填滿了。
Code
#include<cstdio> #include<queue> #include<cstring> #define int long long #define rep(i, a, b) for (register int i=(a); i<=(b); ++i) #define per(i, a, b) for (register int i=(a); i>=(b); --i) using namespace std; const int N=10005, M=200005, inf=1ll<<60; struct node{int to, nxt, flow, cost;}edge[M]; int head[N], vis[N], last[N], pre[N], flow[N], cost[N]; int a[N], s[M], t[M], c[M], n, m, cnt, S, T; void prep(){memset(head, -1, sizeof(head)); cnt=-1;} inline int read() { int x=0,f=1;char ch=getchar(); for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1; for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0'; return x*f; } void add(int u, int v, int f, int w) { edge[++cnt]=(node){v, head[u], f, w}; head[u]=cnt; edge[++cnt]=(node){u, head[v], 0, -w}; head[v]=cnt; } bool spfa() { rep(i, 1, n+3) cost[i]=inf; cost[S]=0; memset(vis, 0, sizeof(vis)); vis[S]=1; rep(i, 1, n+3) flow[i]=inf; queue<int> q; q.push(S); pre[T]=-1; while (!q.empty()) { int u=q.front(); q.pop(); for (int i=head[u]; ~i; i=edge[i].nxt) { vis[u]=0; int v=edge[i].to, w=edge[i].cost, f=edge[i].flow; if (f>0 && cost[u]+w<cost[v]) { pre[v]=u; last[v]=i; cost[v]=cost[u]+w; flow[v]=min(flow[u], f); if (!vis[v]) vis[v]=1; q.push(v); } } } return ~pre[T]; } int EK() { int maxflow=0, mincost=0; while (spfa()) { maxflow+=flow[T]; mincost+=flow[T]*cost[T]; int u=T; while (u^S) { edge[last[u]].flow-=flow[T]; edge[last[u]^1].flow+=flow[T]; u=pre[u]; } } return mincost; } signed main() { prep(); n=read(); m=read(); S=n+2; T=n+3; rep(i, 1, n) a[i]=read(); rep(i, 1, m) s[i]=read(), t[i]=read(), c[i]=read(); add(S, 1, inf, 0); add(n+1, T, inf, 0); rep(i, 1, n) add(i, i+1, inf-a[i], 0); rep(i, 1, m) add(s[i], t[i]+1, inf, c[i]); printf("%lld\n", EK()); return 0; }
NOI2008 誌願者招募