HDU 3572 Task Schedule(ISAP模板&&最大流問題)
阿新 • • 發佈:2017-05-27
reat avi 所有 dsm name col eof 網絡流 -1
題目鏈接:http://acm.hdu.edu.cn/showproblem.php?
pid=3572
題意:m臺機器。須要做n個任務。
第i個任務。你須要使用機器Pi天,且這個任務要在[Si , Ei]區間內完畢才有效。
對於一個任務,僅僅能由一個機器來完畢。一個機器同一時間僅僅能做一個任務。
當然,一個任務能夠分成幾段不連續的時間來完畢。問,是否能做完所有任務。
題意非常清晰。也就是推斷是否是滿流。
對於網絡流問題,模板大家都有,關鍵在於怎樣建圖(詳見資料)
思路:今天問了龍哥,對建圖有了一定的了解,建圖分為4部分,源點->X集合->Y集合->匯點(X、Y類似於二分匹配圖)。確定這個4個部分後,就是找這個4個部分的關系,也就是構建容量網絡
這題的X集合能夠當做任務編號。Y集合當做天數(第幾天)
某任務->某一天。若是能夠在這天做任務。建一條容量為1的邊,最後,把每天到匯點再建一條邊容量M(表示每臺機器最多工作M個任務)即最大容量是M。
以第二組實例作圖(真挫。。)
模板應用的是ISAP(可當做模板)。個人非常傾向ISAP
Accepted | 2292 KB | 62 ms | C++ |
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <math.h> #include <queue> #define init(a) memset(a,0,sizeof(a)) #define PI acos(-1,0) using namespace std; const int maxn = 1100; const int maxm = 400000; #define lson left, m, id<<1 #define rson m+1, right, id<<1|1 #define min(a,b) (a>b)?b:a #define max(a,b) (a>b)?a:b #define MAX INT_MAX int head[maxn], sum, bnum; int dis[maxn]; //殘量網絡中節點 i 到匯點 t 的最短距離 int num[maxn]; //和 t 的最短距離等於 i 的節點數量 int cur[maxn]; //當前弧下標 int pre[maxn]; //可增廣路上的上一條弧的編號 struct node { int v, cap; int next; }edge[maxm]; void add(int u, int v, int cap)//加邊,儲存地圖 { edge[bnum].v=v; edge[bnum].cap=cap; edge[bnum].next=head[u]; head[u]=bnum++; edge[bnum].v = u; edge[bnum].cap=0; edge[bnum].next=head[v]; head[v] = bnum++; } void BFS(int source,int sink)//預處理。利用反向BFS。更新dis數組 { queue<int>q; while(q.empty()==false) q.pop(); memset(num,0,sizeof(num)); memset(dis,-1,sizeof(dis)); q.push(sink); dis[sink]=0; num[0]=1; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(dis[v] == -1) { dis[v] = dis[u] + 1;//找同意弧 num[dis[v]]++; q.push(v); } } } } int ISAP(int source,int sink,int n)//n為殘量網絡中的節點到匯點的最大距離,通常節點的個數。即上限 { memcpy(cur,head,sizeof(cur)); int flow=0, u = pre[source] = source; BFS( source,sink);//更新dis數組 while( dis[source] < n ) { if(u == sink) { int df = MAX, pos; for(int i = source;i != sink;i = edge[cur[i]].v)//追蹤增廣路路徑。最小殘量df { if(df > edge[cur[i]].cap) { df = edge[cur[i]].cap; pos = i; } } for(int i = source;i != sink;i = edge[cur[i]].v) //更新流量 { edge[cur[i]].cap -= df; edge[cur[i]^1].cap += df; } flow += df; u = pos; } int st; for(st = cur[u];st != -1;st = edge[st].next)// 從當前弧開始查找同意弧 { if(dis[edge[st].v] + 1 == dis[u] && edge[st].cap)//找到同意弧跳出 { break; } } if(st != -1) { cur[u] = st; pre[edge[st].v] = u; u = edge[st].v; } else { if( (--num[dis[u]])==0 ) break;//GAP優化,出現斷層結束 int mind = n; for(int id = head[u];id != -1;id = edge[id].next)//retreat操作:更新 dis 數組 { if(mind > dis[edge[id].v] && edge[id].cap) { cur[u] = id;//改動標號的同一時候改動當前弧 mind = dis[edge[id].v]; } } dis[u] = mind+1; num[dis[u]]++; if(u!=source) u = pre[u];// 回溯繼續尋找同意弧 } } return flow; } void initt() { memset(head,-1,sizeof(head)); bnum=0; } int main() { int T, N,M,a,b,c; int maa, sum, source, sink, n; scanf("%d", &T); for (int cas = 1; cas <= T; ++cas) { initt(); sum = 0; source = 0; maa = 0; scanf("%d%d", &N, &M); for (int i = 1; i <= N; i++) { scanf("%d%d%d", &a, &b, &c); sum += a; if(c > maa) maa = c; add(source, i, a); for (int j = b; j <= c; ++j) { add(i, N + j, 1); } } sink = N + maa + 1; n = sink; for (int i = 1; i <= maa; ++i) { add(N + i, sink, M); } printf("Case %d: ", cas); int ans = ISAP(source, sink, n); if(ans==sum) puts("Yes"); else puts("No"); cout<<endl; } return 0; }
HDU 3572 Task Schedule(ISAP模板&&最大流問題)