Oooooooo AAAAE 【網絡流最小點權覆蓋】


Description

 “Let the bass kick!O-oooooooooo AAAAE-A-A-I-A-U- JO-oooooooooooo AAE-O-A-A-U-U-A- E-eee-ee-eee AAAAE-A-E-I-E-A- JO-ooo-oo-oo-oo EEEEO-A-AAA-AAAA!”

LiMn2O4沉迷音樂游戲,每天都在摸魚搓音游,而且是手機電腦兩開花……為了幫助LiMn2O4盡快清醒過來,LiMn2O4答應skyer_hxx,只要skyer_hxx能寫一個自動腳本來玩這個游戲,打敗他的最高紀錄,那么就不再摸魚了。

這個游戲可以簡化成:譜面由N個鍵和M條連線組成,每兩個鍵之間有一個連線,玩家需要在鍵之間滑動,且連線的方向是固定的,玩家每次選擇一個鍵,把所有從這個鍵出發的連線都消除掉,花費為ai,也可以將每個結束在這個點的連線消除,花費Bi。不用擔心,LiMn2O4的手指足夠多。最后讓連線全部消失,得分就是總花費。花費越少,分數越高。

skyer_hxx何許人也,怎么會怕這點小問題?但是他現在很忙,需要你的幫助。請你對於給出的譜面,求出最小的花費。

Input

第一行有兩個正整數N,M,含義同題目描述

接下來兩行,第一行有N個正整數 ,代表從鍵i正向划到別的鍵需要的花費

第二行有N個正整數 ,代表從其他的鍵划到第i個鍵需要的花費

接下來M行,每一行都有兩個正整數u,v,代表鍵u,v之間有一條u到v的連線。兩個鍵之間可能有多個連線,同一個鍵之間也可能有連線。

Output

輸出最小的花費,占一行。

Sample Input

3 2

1 2 1

2 1 2

1 2

1 3

Sample Output

2

我的想法:

1.首先想用貪心算法,存每條邊然后比較out[a], in[b]。不過錯的很離譜,答案要大了很多。原因是因為沒看清楚題目。也就是上面我標紅的地方。對於每個點只能計算一次最小的點權,用貪心的話點有重復也會重復計算進去。

2.最小點權覆蓋集:從x或者y集合中選取一些點,使這些點覆蓋所有的邊,並且選出來的點的權值盡可能小。最大點權獨立集:在二分圖中找到權值和最大的點集,使得它們之間兩兩沒有邊。

3.借鑒了題解的思路。才知道是網絡流最小點權覆蓋問題設立源點s和t,s連邊到點i,容量為i點的權值;點j連邊到t,容量為j點權值;原二分圖中的邊容量為INF,求最大流即為最小點權覆蓋。

4.擴展:最大點權獨立集可轉化為最小點權覆蓋問題,最大點權獨立集 = 總權值 - 最小點權覆蓋集

 

題解思路:

經典的網絡流最小點權覆蓋問題,首先拆點,分成左右兩部分,源點向所有左節點連一條邊,流量為刪除出邊的花費。所有右節點向匯點連一條邊,流量為刪除入邊的費用。對於每條輸入給定的邊<u,v>,其在左邊的點為u,v,右邊的點為u’,v’,那么我們連一條<u,v’>的邊,流量為∞(正無窮才不會對可行流上最小的容量產生限制)。跑一邊最大流,得到的答案就是最小的費用。
首先,如果沒有花費的話,這道題就是簡單的最小點覆蓋集。加上了點權之后,我們就要將最小割的思想融合進去。要注意費用,一定要符合S->T的順序

代碼:

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<queue>
  4 #include<algorithm>
  5 #define mem(a, b) memset(a, b, sizeof(a))
  6 const int inf = 0x3f3f3f3f;
  7 using namespace std;
  8 
  9 int n, m, cnt, head[250];
 10 int arr[110], brr[110];
 11 int dep[250];
 12 queue<int>Q;
 13 
 14 struct Edge
 15 {
 16     int to, next;
 17     int w; 
 18 }edge[20000];
 19 
 20 void add(int a, int b, int w)
 21 {
 22     edge[cnt].to = b;
 23     edge[cnt].w = w;
 24     edge[cnt].next = head[a];
 25     head[a] = cnt ++;
 26 }
 27 
 28 int bfs(int st, int ed) //dinic算法的優化之處,先進行分層 
 29 {
 30     if(st == ed)
 31         return 0;
 32     while(!Q.empty())
 33         Q.pop();
 34     mem(dep, -1);  //每次都初始化為未被分層 
 35     dep[st] = 1; //源點設置層次為1
 36     Q.push(st);
 37     while(!Q.empty())
 38     {
 39         int index = Q.front();
 40         Q.pop();
 41         for(int i = head[index]; i != -1; i = edge[i].next)
 42         {
 43             int to = edge[i].to;
 44             if(dep[to] == -1 && edge[i].w > 0)
 45             {
 46                 dep[to] = dep[index] + 1;
 47                 Q.push(to);
 48             }
 49         }
 50     }
 51     return dep[ed] != -1;//返回是否成功分層 
 52 }
 53 
 54 int dfs(int now, int ed, int cnt)
 55 {
 56     if(now == ed)
 57         return cnt;
 58     for(int i = head[now]; i != -1; i = edge[i].next)
 59     {
 60         int to = edge[i].to;
 61         if(dep[to] == dep[now] + 1 && edge[i].w > 0)
 62         {
 63             int flow = dfs(to, ed, min(edge[i].w, cnt));
 64             if(flow > 0)
 65             {
 66                 edge[i].w -= flow;
 67                 edge[i ^ 1].w += flow;
 68                 return flow;
 69             }
 70         }
 71     }
 72     return -1;
 73 }
 74 
 75 void dinic(int st, int ed)
 76 {
 77     long long ans = 0;
 78     while(bfs(st, ed))
 79     {
 80         while(1)
 81         {
 82             int inc = dfs(st, ed, inf);
 83             if(inc == -1)
 84                 break;
 85             ans += inc;
 86         }
 87     }
 88     printf("%lld\n", ans);
 89 }
 90 
 91 int main()
 92 {
 93     mem(head, -1);
 94     scanf("%d%d", &n, &m);//n個點,m條邊 
 95     for(int i = 1; i <= n; i ++) //起點向匯點 2 * n + 1建邊
 96     {
 97         scanf("%d", &arr[i]);
 98         add(i + n, 2 * n + 1, arr[i]);
 99         add(2 * n + 1, i + n, 0);
100     }
101     for(int i = 1; i <= n; i ++)//源點 0 向終點建邊 
102     {
103         scanf("%d", &brr[i]);
104         add(0, i, brr[i]);
105         add(i, 0, 0);
106     }
107     for(int i = 1; i <= m; i ++)//從源點方向向匯點方向建邊
108     {
109         int a, b;
110         scanf("%d%d", &a, &b);
111         add(a, b + n, inf);
112         add(b + n, a, 0);
113     }
114     //從源點向匯點跑一遍最大流
115     dinic(0, 2 * n + 1); 
116     return 0;
117 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM