NOI2012 美食节 解题报告
首先看看BZOJ 1040《修车》,如果没有做,请做完后再来看这道题。
我们惊喜地发现,这道题的题意跟 修车 基本一样。很可惜,数据范围。。。
我试了一下直接修改 修车 的代码,建成2+n+p*m个点,n*m*p条边的有向图,对其求费用流,时间复杂度O((2+n+p*m)^2*(n*m*p)),实际测试中得到40分。
既然不能做这么庞大的图的网络流,我们考虑动态加点。
刚开始连边:
现在,所有厨师都闲着,那我们把每位厨师做倒数第一道菜的边连向N个点。
然后做费用流。
在增广的过程中,如果k厨师做倒数第i个菜连到汇点满流,则每个菜对k厨师做倒数第i+1个菜连一条边,流量为1,权值为(i+1) * t[][k]。这样做到动态加边,避免因为边数过多导致超时。
谢谢HSJMDMG大神在其博客中详细地写到如何动态加点
http://hi.baidu.com/hsjm_mushroom/item/5927e18558e85855840fab8e
下面是在BZOJ中通过的代码(实际测试中只获得了80%的分数,待优化):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 5 #define N 803 6 #define M 102 7 #define K 327680 8 9 using namespace std; 10 11 struct EDGE{ 12 int adj, flow, cost, next; 13 } edge[K]; 14 int n, m, k, p[N], S, T, dist[K], pre[K], prev[K], top; 15 int t[N][M], gh[K], q[K], v[K], cnt[M]; 16 17 void addedge(int x, int y, int flow, int cost) 18 { 19 edge[++top].adj = y; 20 edge[top].flow = flow; 21 edge[top].cost = cost; 22 edge[top].next = gh[x]; 23 gh[x] = top; 24 edge[++top].adj = x; 25 edge[top].flow = 0; 26 edge[top].cost = -cost; 27 edge[top].next = gh[y]; 28 gh[y] = top; 29 } 30 31 int inc(int &x) {return (x += 1) %= K;} 32 33 int spfa() 34 { 35 int head = -1, tail = 0; 36 memset(dist, 0x7f, sizeof(dist[0])*(k*m+n+3)); 37 q[0] = S; dist[S] = 0; 38 while (head != tail) 39 { 40 int h = q[inc(head)]; v[h] = 0; 41 for (int p = gh[h]; p; p=edge[p].next) 42 if (edge[p].flow && edge[p].cost + dist[h] < dist[edge[p].adj]) 43 { 44 dist[edge[p].adj] = edge[p].cost + dist[h]; 45 pre[edge[p].adj] = h; 46 prev[edge[p].adj] = p; 47 if (!v[edge[p].adj]) 48 { 49 v[edge[p].adj] = 1; 50 q[inc(tail)] = edge[p].adj; 51 } 52 } 53 } 54 return dist[T] <= 0x7f000000; 55 } 56 57 int flow() 58 { 59 int m = 0x7f7f7f7f, qans = 0; 60 for (int i=T;i!=S;i=pre[i]) 61 if (edge[prev[i]].flow < m) m = edge[prev[i]].flow; 62 for (int i=T;i!=S;i=pre[i]) 63 { 64 edge[prev[i]].flow -= m; 65 edge[prev[i]^1].flow += m; 66 qans += m * edge[prev[i]].cost; 67 } 68 int ptr = pre[T] + 1; 69 int x = pre[T] - 2 - n; 70 ((x -= 1) /= k) += 1; 71 if (cnt[x]==k) return qans; 72 ++cnt[x]; 73 for (int i=1;i<=n;i++) 74 addedge(i+2, 2+n+(x-1)*k+cnt[x], 1, t[i][x] * (cnt[x])); 75 return qans; 76 } 77 78 int main() 79 { 80 #ifndef ONLINE_JUDGE 81 freopen("delicacy.in","r",stdin); 82 freopen("delicacy.out","w",stdout); 83 #endif 84 scanf("%d%d",&n,&m); 85 k = 0; 86 for (int i=1;i<=n;i++) 87 { 88 scanf("%d",&p[i]); 89 k += p[i]; 90 } 91 for (int i=1;i<=m;i++) 92 cnt[i] = 1; 93 for (int i=1;i<=n;i++) 94 for (int l1=1;l1<=m;l1++) 95 scanf("%d",&t[i][l1]); 96 S = 1, T = 2; top = 1; 97 for (int i=1;i<=n;i++) 98 addedge(S, i+2, p[i], 0); 99 for (int i=0;i<m;i++) 100 for (int j=1;j<=k;j++) 101 addedge(i*k+j+2+n, T, 1, 0); 102 for (int l=1;l<=n;l++) 103 for (int i=0;i<m;i++) 104 addedge(l+2, i*k+3+n, 1, t[l][i+1]); 105 int ans = 0; 106 while (spfa()) 107 ans += flow(); 108 printf("%d\n",ans); 109 return 0; 110 }