首页 > 代码库 > NOI2012 美食节 解题报告

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%的分数,待优化):

  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 }
View Code: delicacy.cpp