【费用流】BZOJ1061: [Noi2008]志愿者招募(这题超好)
生活随笔
收集整理的這篇文章主要介紹了
【费用流】BZOJ1061: [Noi2008]志愿者招募(这题超好)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1061: [Noi2008]志愿者招募
Time Limit: 20 Sec??Memory Limit: 162 MBSubmit: 5291??Solved: 3173
[Submit][Status][Discuss]
Description
申奧成功后,布布經過不懈努力,終于成為奧組委下屬公司人力資源部門的主管。布布剛上任就遇到了一個難 題:為即將啟動的奧運新項目招募一批短期志愿者。經過估算,這個項目需要N 天才能完成,其中第i 天至少需要 Ai 個人。 布布通過了解得知,一共有M 類志愿者可以招募。其中第i 類可以從第Si 天工作到第Ti 天,招募費用 是每人Ci 元。新官上任三把火,為了出色地完成自己的工作,布布希望用盡量少的費用招募足夠的志愿者,但這 并不是他的特長!于是布布找到了你,希望你幫他設計一種最優的招募方案。Input
第一行包含兩個整數N, M,表示完成項目的天數和可以招募的志愿者的種類。 接下來的一行中包含N 個非負 整數,表示每天至少需要的志愿者人數。 接下來的M 行中每行包含三個整數Si, Ti, Ci,含義如上文所述。為了 方便起見,我們可以認為每類志愿者的數量都是無限多的。Output
僅包含一個整數,表示你所設計的最優方案的總費用。
Sample Input
3 32 3 4
1 2 2
2 3 5
3 3 2
Sample Output
14HINT
1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,題目中其他所涉及的數據均 不超過2^31-1。
題解
這題dalao們要不就是用線性規劃來把不定方程轉化為一般方程建邊跑費用流
但是有dalao提出了這樣一種建邊方法(我們用二元組(cap,val)表示邊的流量和花費):
- 對于每一天向后一天連邊(inf?ai,0)
- 對于每一種志愿者選擇,從L到R+1建邊(inf,cost[i])
- 從超級源向第一天連邊(inf,0)
- 從最后一天+1向超級匯連邊(inf,0)
?
但是為什么這樣是對的呢?
原諒本蒟蒻看了許多dalao們的解釋都看不懂。。。
于是我就自己YY了一下:
- 假設有一種免費志愿者,他的工作區間為(1,n)
- 我們每天都需要inf個志愿者,其中有至少a[i]個非免費志愿者,也就是最多有inf-a[i]個免費志愿者
- 所以我們對于每一天向它的下一天連邊(inf-a[i],0)意味著這一天工作完下一天繼續工作的免費志愿者最多為inf-a[i]個(因為中間不能臨時增加免費志愿者)
- 然后對于每個非免費志愿者i的工作區間(l,r),我們從l向r+1連一條邊(inf,c[i])表示我們這幾天可以花費每人c[i]的代價讓免費志愿者直接去往第r+1天,而剩下的部分由非免費志愿者補齊
- 建立超級源點向第一天連(inf,0)表示第一天可以有inf個免費志愿者
- 從第n+1天向超級匯點連(inf,0)表示最后每天都要有inf個志愿者
- 這樣就可以把有下限無上限的問題轉化成有上限無下限的問題
跑費用流
代碼
//by 減維 #include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<bitset> #include<set> #include<cmath> #include<vector> #include<set> #include<map> #include<ctime> #include<algorithm> #define ll long long #define db double #define inf 2147483647//1<<29 #define maxn 20005 #define eps 1e-8 using namespace std;inline int read() {int ret=0;bool fla=0;char ch=getchar();while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();if(ch=='-'){fla=1;ch=getchar();}while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}return fla?-ret:ret; }struct edge{int to,ne,cap,val; }e[maxn<<6];int n,m,s,t,ecnt=1,a[maxn],head[maxn],dis[maxn]; bool pd[maxn],vis[maxn];void add(int x,int y,int z,int k) {ecnt++;e[ecnt]=(edge){y,head[x],z,k};head[x]=ecnt;ecnt++;e[ecnt]=(edge){x,head[y],0,-k};head[y]=ecnt; }bool bfs() {deque<int>q;q.push_back(t);for(int i=s;i<=t;++i)dis[i]=inf;dis[t]=0;memset(pd,0,sizeof pd);pd[t]=1;while(!q.empty()){int d=q.front();q.pop_front();pd[d]=0;for(int i=head[d];i;i=e[i].ne){int dd=e[i].to;if(e[i^1].cap&&dis[dd]>dis[d]-e[i].val){dis[dd]=dis[d]-e[i].val;if(!pd[dd]){pd[dd]=1;if(q.empty()||dis[dd]>dis[q.front()]) q.push_back(dd);else q.push_front(dd);}}}}return dis[s]<inf; }int dfs(int x,int cap) {vis[x]=1;if(x==t||!cap)return cap;int tmp,ret=0;for(int i=head[x];i;i=e[i].ne){int dd=e[i].to;if(!vis[dd]&&e[i].cap&&dis[dd]==dis[x]-e[i].val){tmp=dfs(dd,min(e[i].cap,cap));cap-=tmp;ret+=tmp;e[i].cap-=tmp;e[i^1].cap+=tmp;}}return ret; }int zkw() {int ret=0;while(bfs()){vis[t]=1;while(vis[t]){memset(vis,0,sizeof vis);ret+=dfs(s,inf)*dis[s];}}return ret; }int main() {n=read();m=read();s=0,t=n+2;for(int i=1;i<=n;++i) a[i]=read(),add(i,i+1,inf-a[i],0);for(int i=1,x,l,r;i<=m;++i) l=read(),r=read(),x=read(),add(l,r+1,inf,x);add(s,1,inf,0);add(n+1,t,inf,0);printf("%d",zkw());return 0; }?
轉載于:https://www.cnblogs.com/rir1715/p/8195733.html
總結
以上是生活随笔為你收集整理的【费用流】BZOJ1061: [Noi2008]志愿者招募(这题超好)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 邓迎春绘画201702作品10
- 下一篇: 说一下自己对于 Linux 哲学的理解