这道题,我觉得难想到的是 “If there are multiple sets of truck routes that achieve the goal at minimum cost, choose one that shuts down the minimum number of routes.”,也就是如何在多个最小割中,取边数最少的方案。怎么处理这个条件,我想到的方法有点像像用背包问题:先求出最小割P。然后判断路线1到路线M,该路线是否可以在最小割中(判断方式是:去掉该边后的最小这P',如果P-P‘==lenth[i],则该边可以在最小割中),所有满足条件的边组成集合S,下面就是背包问题了,容量为P,从S中取边,用dfs取得所有解,选边数最小,字典序最小的即可。
后来看过别人关于第二问的讨论之后,又一次意识到,我还是太弱了。通过设置权重,len[i]*1001 + 1 设为新的边长,因为边数最多为1000,所以这个+1并不会影响最小割的选择,同时这个+1又可以保证边数最少。于是,我们求出的新的最小割P,用P/1001 即为原始的最小割,P%1001则为此次最小割的边数。第三问,因为已经确定了边数最小,我们只要保证字典序最小即可:判断边i是否可以在最小割中,如可以,将此边从图中删除更新最小割,直至最小割变为0为止。
另外还需要注意的就是,两个仓库之间可以有多条路线~
时间复杂度为O(n^4), N最大为30,所以肯定不会超时的。
附代码:
/* ID: zhangyc1 LANG: C++ TASK: milk6 */ #include <fstream> #include <iostream> #include <string> #include <cstring> #include <cstdlib> using namespace std; struct Line { int nSt, nEd, nCost; }; ofstream fileout("milk6.out"); int arrVisitList[31], arrVisitList2[31];// 访问队列 int arrNext[33], arrNext2[33];// 每个节点的已访问位置 long long arrCap[33][33], arrFlow[33][33], arrExtra[33];// 容量,流量,余流 long long arrCap2[33][33], arrFlow2[33][33], arrExtra2[33]; int arrHeight[33], arrHeight2[33];// 高度 const int C_W = 1001; int N, M; const int MAX_HEIGHT = 1000000; Line arrLine[1000]; long long RelabelToFront(); void prepairData() { memset(arrCap, 0, sizeof(arrCap)); ifstream filein("milk6.in"); filein >> N >> M; for (int i = 0; i < M; i++) { filein >> arrLine[i].nSt >> arrLine[i].nEd >> arrLine[i].nCost; // 这个..两个仓库间可能有多个线路 arrCap[arrLine[i].nSt][arrLine[i].nEd] += arrLine[i].nCost * C_W + 1; } filein.close(); for (int i = 1; i <= N; i++) { arrNext[i] = 1; } for (int i = 0; i < N - 1; i++) { arrVisitList[i] = i + 1; } memset(arrFlow, 0, sizeof(arrFlow)); memset(arrExtra, 0, sizeof(arrExtra)); memset(arrHeight, 0, sizeof(arrHeight)); memcpy(arrVisitList2, arrVisitList, sizeof(arrVisitList)); memcpy(arrNext2, arrNext, sizeof(arrNext)); memcpy(arrCap2, arrCap, sizeof(arrCap)); memcpy(arrFlow2, arrFlow, sizeof(arrFlow)); memcpy(arrExtra2, arrExtra, sizeof(arrExtra)); memcpy(arrHeight2, arrHeight, sizeof(arrHeight)); } void RestoreData() { memcpy(arrVisitList, arrVisitList2, sizeof(arrVisitList)); memcpy(arrNext, arrNext2, sizeof(arrNext)); memcpy(arrCap, arrCap2, sizeof(arrCap)); memcpy(arrFlow, arrFlow2, sizeof(arrFlow)); memcpy(arrExtra, arrExtra2, sizeof(arrExtra)); memcpy(arrHeight, arrHeight2, sizeof(arrHeight)); } void process() { long long rs = RelabelToFront(); long long nMinCost = rs / C_W; long long nNumLine = rs % C_W; fileout << nMinCost << " " << nNumLine << endl; if (nNumLine == 0) return; for (int i = 0; i < M; i++) { // 删除此条线路,判断其是否在最大流中 arrCap[arrLine[i].nSt][arrLine[i].nEd] -= arrLine[i].nCost* C_W + 1; arrCap2[arrLine[i].nSt][arrLine[i].nEd] = arrCap[arrLine[i].nSt][arrLine[i].nEd]; long long newRs = RelabelToFront(); if (rs - newRs == arrLine[i].nCost* C_W + 1) { fileout << i + 1 << endl; rs = newRs; if (newRs == 0) break; } else {// 若不在,恢复数据 arrCap[arrLine[i].nSt][arrLine[i].nEd] += arrLine[i].nCost* C_W + 1; arrCap2[arrLine[i].nSt][arrLine[i].nEd] = arrCap[arrLine[i].nSt][arrLine[i].nEd]; } } } int main(){ prepairData(); process(); fileout.close(); return 0; } // 压入 void PushOper(int u, int v) { long long nMin = min(arrExtra[u], arrCap[u][v] - arrFlow[u][v]); arrExtra[u] -= nMin, arrExtra[v] += nMin; arrFlow[u][v] += nMin, arrFlow[v][u] -= nMin; } // 重标注 void RelabelOper(int u) { int nMin = MAX_HEIGHT; for (int i = 1; i <= N; i++) { if (arrCap[u][i] > arrFlow[u][i] && nMin > arrHeight[i]) nMin = arrHeight[i]; } arrHeight[u] = nMin + 1; } // 排空 void Discharge(int u) { //cout << "Discharge u" << u << endl; while (arrExtra[u] > 0) { if (arrNext[u] <= N) { int v = arrNext[u]; if (arrHeight[u] > arrHeight[v] && arrCap[u][v] > arrFlow[u][v]) PushOper(u, v); else arrNext[u]++; } else { RelabelOper(u); arrNext[u] = 1; } } } // 前移 void MoveToFront(int nIndex) { int nKey = arrVisitList[nIndex]; for (int i = nIndex; i; i--) { arrVisitList[i] = arrVisitList[i - 1]; } arrVisitList[0] = nKey; } // 重标注前移算法。结束前将数据恢复 long long RelabelToFront() { arrHeight[1] = N; for (int i = 2; i <= N; i++) { if (arrCap[1][i] > 0) { arrExtra[1] -= arrCap[1][i]; arrExtra[i] = arrCap[1][i]; arrFlow[1][i] = arrCap[1][i]; arrFlow[i][1] = -arrFlow[1][i]; } } int nIndex = 1; while (nIndex < N - 1) { int u = arrVisitList[nIndex]; int nOldHeight = arrHeight[u]; Discharge(u); int nNewHeight = arrHeight[u]; if (nOldHeight != nNewHeight) { MoveToFront(nIndex); nIndex = 1; } else nIndex++; } long long rs = arrExtra[N]; RestoreData(); return rs; }