日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【POJ - 2987】Firing(最大权闭合图,网络流最小割,输出方案最小,放大权值法tricks)

發布時間:2023/12/10 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【POJ - 2987】Firing(最大权闭合图,网络流最小割,输出方案最小,放大权值法tricks) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題干:

You’ve finally got mad at “the world’s most stupid” employees of yours and decided to do some firings. You’re now simply too mad to give response to questions like “Don’t you think it is an even more stupid decision to have signed them?”, yet calm enough to consider the potential profit and loss from firing a good portion of them. While getting rid of an employee will save your wage and bonus expenditure on him, termination of a contract before expiration costs you funds for compensation. If you fire an employee, you also fire all his underlings and the underlings of his underlings and those underlings’ underlings’ underlings… An employee may serve in several departments and his (direct or indirect) underlings in one department may be his boss in another department. Is your firing plan ready now?

Input

The input starts with two integers?n?(0 <?n?≤ 5000) and?m?(0 ≤?m?≤ 60000) on the same line. Next follows?n?+?m?lines. The first?n?lines of these give the net profit/loss from firing the?i-th employee individually?bi?(|bi| ≤ 107, 1 ≤?i?≤?n). The remaining?m?lines each contain two integers?i?and?j?(1 ≤?i,?j?≤?n) meaning the?i-th employee has the?j-th employee as his direct underling.

Output

Output two integers separated by a single space: the minimum number of employees to fire to achieve the maximum profit, and the maximum profit.

Sample Input

5 5 8 -9 -20 12 -10 1 2 2 5 1 4 3 4 4 5

Sample Output

2 2

Hint

As of the situation described by the sample input, firing employees 4 and 5 will produce a net profit of 2, which is maximum.

題目大意:

公司解雇員工,每個員工有一個權值,可正可負可為零(為正代表解雇員工可以獲得的利潤,為負代表獲得的利潤為負)。然后給出一些員工上下級的關系,如果解雇一個員工(比如經理主管之類的),那么他手下的所有員工都會被解雇。注意一點:公司有很多部門,每個人可能不只效力于一個部門,所以有可能在這個部門A是B的上司,在另一個部門內B是A的上司。(其實就是代表圖中可能有環)問對公司獲利最大的解雇計劃以及最少的解雇員工人數。

解題報告:

? 看起來就是個最大權閉合圖。但是不知道怎么證明這樣得到的就是最少的解雇人數了、、

AC代碼:

#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define F first #define S second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 5e5 + 5; int n,m; int tot; const ll INF = 0x3f3f3f3f3f3f3f3f; struct Edge {int to,ne;ll w; } e[MAX]; int head[MAX]; int st,ed; ll a[MAX],dis[MAX]; int q[MAX];//一共多少個點跑bfs,dis數組和q數組就開多大。 void add(int u,int v,ll w) {e[++tot].to=v;e[tot].w=w;e[tot].ne=head[u];head[u]=tot; } bool bfs(int st,int ed) {memset(dis,-1,sizeof(dis));int front=0,tail=0;q[tail++]=st;dis[st]=0;while(front<tail) {int cur = q[front];if(cur == ed) return 1;front++;for(int i = head[cur]; i!=-1; i = e[i].ne) {if(e[i].w&&dis[e[i].to]<0) {q[tail++]=e[i].to;dis[e[i].to]=dis[cur]+1;}}}if(dis[ed]==-1) return 0;return 1; } ll dfs(int cur,ll limit) {//limit為源點到這個點的路徑上的最小邊權 if(limit==0||cur==ed) return limit;ll w,flow=0;for(int i = head[cur]; i!=-1; i = e[i].ne) { if(e[i].w&&dis[e[i].to]==dis[cur]+1) {w=dfs(e[i].to,min(limit,e[i].w));e[i].w-=w;e[i^1].w+=w;flow+=w;limit-=w;if(limit==0) break;}}if(!flow) dis[cur]=-1;return flow; } ll dinic() {ll ans = 0;while(bfs(st,ed)) ans+=dfs(st,INF);//0x7fffffff可能就不對了? return ans; } int ans2; bool vis[MAX]; void dfs2(int cur) {if(cur == ed) return;vis[cur] = 1;ans2++;for(int i = head[cur]; ~i; i = e[i].ne) {int v = e[i].to;if(e[i].w == 0 || vis[v]) continue;dfs2(v);} } int main() {cin>>n>>m;st=0;ed=n+1;tot=1;ll sum = 0;for(int i = 0; i<=n+1; i++) head[i] = -1;for(int i = 1; i<=n; i++) {scanf("%lld",a+i);if(a[i] > 0) sum += a[i],add(st,i,a[i]),add(i,st,0);if(a[i] < 0) add(i,ed,-a[i]),add(ed,i,0);}for(int a,b,i = 1; i<=m; i++) {scanf("%d%d",&a,&b);add(a,b,INF);add(b,a,0);}ll ans = sum - dinic();dfs2(st);printf("%d %lld\n",ans2-1,ans);return 0; }

玄學方法:

這題的特殊之處就是要輸出最少辭退員工數。
怎么辦呢?

利用一個經典的trick:多關鍵字
建圖前,對所有b[i],執行變換b[i]=b[i]*10000-1,然后,會驚異地發現,
此時最大流所對應的方案就是滿足辭退最少人數的了。
為什么?顯然,變換后的流量r2除以10000后再取整就等于原來的流量,但是
r2的后四位卻蘊含了辭退人數的信息:每多辭退一個人,流量就會少1。

剩下的就是如何根據一個網絡流輸出方案。
我的做法:從源點開始沿著殘余網絡dfs(只走沒有滿載的邊),
能dfs到的點對應的人就是需要辭退的。

int main() {cin>>n>>m;st=0;ed=n+1;tot=1;ll sum = 0;for(int i = 0; i<=n+1; i++) head[i] = -1;for(int i = 1; i<=n; i++) {scanf("%lld",a+i);a[i] = a[i]*10000-1;if(a[i] > 0) sum += a[i],add(st,i,a[i]),add(i,st,0);if(a[i] < 0) add(i,ed,-a[i]),add(ed,i,0);}for(int a,b,i = 1; i<=m; i++) {scanf("%d%d",&a,&b);add(a,b,INF);add(b,a,0);}ll ans = sum - dinic();dfs2(st);printf("%d %lld\n",ans2-1,(ans+9999)/10000);return 0; }

或者不用dfs2:

int main() {cin>>n>>m;st=0;ed=n+1;tot=1;ll sum = 0;for(int i = 0; i<=n+1; i++) head[i] = -1;for(int i = 1; i<=n; i++) {scanf("%lld",a+i);a[i] = a[i]*10000 - 1;if(a[i] > 0) sum += a[i],add(st,i,a[i]),add(i,st,0);if(a[i] < 0) add(i,ed,-a[i]),add(ed,i,0);}for(int a,b,i = 1; i<=m; i++) {scanf("%d%d",&a,&b);add(a,b,INF);add(b,a,0);}ll ans = sum - dinic(); // ans2 = -(ans % 10000 - 10000) % 10000 ;//這兩個用哪個都可以ans2 = (10000 - ans%10000)%10000 ;//別忘最后要%10000!!不然ans==10000的時候就WA了ans = (ans+ans2)/10000; // dfs2(st);printf("%d %lld\n",ans2,ans);return 0; }

?

總結

以上是生活随笔為你收集整理的【POJ - 2987】Firing(最大权闭合图,网络流最小割,输出方案最小,放大权值法tricks)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。