ACM-ICPC 2018 焦作賽區網路預賽 F. Modular Production Line(最小費用流)
阿新 • • 發佈:2018-11-19
題意:
給你n個點,每一個點最多被用k次,每一次任務需要同時使用連續編號的若干個點,任務完成相應獲得價值,
並且所使用的點的使用次數-1,問你最大的價值多少
解析:
想了一個下午,想不出怎麼建圖,甚至懷疑是不是用最小費用流做的,後來晚上靈光一現。
就最普通的先建一條0-cm+2的鏈(0:源點,cm+2匯點),鏈上的邊的流量都為K(cm是縮完點的點的數量),然後每一個任務的持續持續時間u,v
建立一條u->v+1的權值為w+1的邊(v+1是因為避免一個點是一個任務的開頭,同時是另一個任務的結尾的情況)
然後跑一遍最小費用流就可以了。
這裡的原理我總結成兩句話1.任務交叉就共用流量k 2.任務不交叉就並用流量k
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> using namespace std; #define INFINITE 1 << 26 #define INF 0x3f3f3f3f #define MAX_NODE 420 #define MAX_EDGE_NUM 80005 struct Edge{ int to; int vol; int cost; int next; }; Edge gEdges[MAX_EDGE_NUM]; int gHead[MAX_NODE]; int gPre[MAX_NODE]; int gPath[MAX_NODE]; int gDist[MAX_NODE]; int has[100010]; int qe[MAX_NODE][3]; int gEdgeCount; void InsertEdge(int u, int v, int vol, int cost){ gEdges[gEdgeCount].to = v; gEdges[gEdgeCount].vol = vol; gEdges[gEdgeCount].cost = cost; gEdges[gEdgeCount].next = gHead[u]; gHead[u] = gEdgeCount++; gEdges[gEdgeCount].to = u; gEdges[gEdgeCount].vol = 0; //vol為0,表示開始時候,該邊的反向不通 gEdges[gEdgeCount].cost = -cost; //cost 為正向邊的cost相反數,這是為了 gEdges[gEdgeCount].next = gHead[v]; gHead[v] = gEdgeCount++; } //假設圖中不存在負權和環,SPFA演算法找到最短路徑/從源點s到終點t所經過邊的cost之和最小的路徑 bool Spfa(int s, int t){ memset(gPre, -1, sizeof(gPre)); memset(gDist, -INF, sizeof(gDist)); gDist[s] = 0; queue<int> Q; Q.push(s); while (!Q.empty()){//由於不存在負權和環,因此一定會結束 int u = Q.front(); Q.pop(); for (int e = gHead[u]; e != -1; e = gEdges[e].next){ int v = gEdges[e].to; if (gEdges[e].vol > 0 && gDist[u] + gEdges[e].cost > gDist[v]){ gDist[v] = gDist[u] + gEdges[e].cost; gPre[v] = u; //前一個點 gPath[v] = e;//該點連線的前一個邊 Q.push(v); } } } if (gPre[t] == -1) //若終點t沒有設定pre,說明不存在到達終點t的路徑 return false; return true; } int MinCostFlow(int s, int t){ int cost = 0; int flow = 0; while (Spfa(s, t)){ int f = INFINITE; for (int u = t; u != s; u = gPre[u]){ if (gEdges[gPath[u]].vol < f) f = gEdges[gPath[u]].vol; } flow += f; cost += gDist[t] * f; for (int u = t; u != s; u = gPre[u]){ gEdges[gPath[u]].vol -= f; //正向邊容量減少 gEdges[gPath[u]^1].vol += f; //反向邊容量增加 } } return cost; } int dnum[MAX_NODE]; int main() { int t,n; int S,Final; scanf("%d",&t); int m,K,w; while(t--) { memset(gHead,-1,sizeof(gHead)); gEdgeCount=0; //memset(has,0,sizeof(has)); scanf("%d%d%d",&n,&K,&m); int cnt=1; for(int i=1;i<=m;i++) { scanf("%d%d%d",&qe[i][0],&qe[i][1],&qe[i][2]); dnum[cnt++]=qe[i][0]; dnum[cnt++]=qe[i][1]; } sort(dnum+1,dnum+cnt); has[dnum[1]]=1; int cm=1; for(int i=2;i<cnt;i++) { if(dnum[cm]!=dnum[i]) { dnum[++cm]=dnum[i]; has[dnum[i]]=cm; } } S=0; Final=cm+2; InsertEdge(0,1,K,0); for(int i=1;i<=cm+1;i++) { InsertEdge(i,i+1,K,0); } //int task=Final+1; for(int i=1;i<=m;i++) { int u,v,w; u=has[qe[i][0]]; v=has[qe[i][1]]; w=qe[i][2]; v++; //InsertEdge(u,task,1,0); //InsertEdge(task,v,1,w); InsertEdge(u,v,1,w); //task++; } int ans=MinCostFlow(S,Final); printf("%d\n",ans); } }