bzoj3144 [Hnoi2013]切糕

  这个题是个挺经典的最小割。

  这个题的关键是如何限制h之差不超过d。首先我们按高度分层,每层的点向下一层相同位置的点连边,边权设为点权(也就是说我们要多设一层),然后如果我们割掉这条边就意味着选择了下面这个点。然后,对于h之差的限制,我们把k+d层的点向k层的四周的点连+oo边,也就是说如果我们割掉了一条边,就不能选择+oo的边连接的上面的边,因为选择了这条边,如果再选择上面的边的话,就不能构成割了,因为流还是可以经过那条+oo的边流回来。其实类比一下最大权独立集的话,这条+oo的边的意义就是选了某个点以后,就不能选和它相差超过d的点了。

  最后跑最小割就ok了。

 

  

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<cstring>
 6 #define maxn 80000
 7 #define maxm 3000000
 8 #define inf 2147483647
 9 using namespace std;
10 struct et
11 {
12     int s,t,val,next;
13 }e[maxm];
14 const int dx[4]={0,1,0,-1};
15 const int dy[4]={1,0,-1,0};
16 int fir[maxn],dis[maxn],gap[maxn],last[maxn];
17 int v,s[60][60][60];
18 int st,ed,n,m,h,num,tot,D,cnt;
19 
20 int dfs(int now,int flow)
21 {
22     if (now==ed) return flow;
23     int sap=0;
24     for (int j=last[now];j;j=e[j].next)
25     {
26         int k=e[j].t;
27         if (e[j].val&&dis[now]==dis[k]+1)
28         {
29             last[now]=j;
30             int tmp=dfs(k,min(e[j].val,flow-sap));
31             e[j].val-=tmp;
32             e[j^1].val+=tmp;
33             sap+=tmp;
34             if (sap==flow) return sap;
35         }
36     }
37     if (dis[st]>=num) return sap;
38     if (!(--gap[dis[now]])) dis[st]=num;
39     ++gap[++dis[now]];
40     last[now]=fir[now];
41     return sap;
42 }
43 
44 void add(int x,int y,int z)
45 {
46     e[++tot].s=x; e[tot].t=y; e[tot].val=z; e[tot].next=fir[x]; fir[x]=tot;
47     e[++tot].s=y; e[tot].t=x; e[tot].val=0; e[tot].next=fir[y]; fir[y]=tot;
48 }
49 
50 int main()
51 {
52     //freopen("cake.in","r",stdin);
53     //freopen("cake.out","w",stdout);
54     scanf("%d%d%d",&n,&m,&h);
55     scanf("%d",&D);
56     for (int k=1;k<=h+1;k++)
57         for (int i=1;i<=n;i++)
58             for (int j=1;j<=m;j++)
59                 s[k][i][j]=++cnt;
60     st=0; ed=cnt+1; num=cnt+2; tot=1;
61     for (int i=1;i<=n;i++)
62         for (int j=1;j<=m;j++)
63             add(st,s[1][i][j],inf),add(s[h+1][i][j],ed,inf);
64     for (int k=1;k<=h;k++)
65         for (int i=1;i<=n;i++)
66             for (int j=1;j<=m;j++)
67                 scanf("%d",&v),add(s[k][i][j],s[k+1][i][j],v);
68     for (int k=1;k<=h;k++)
69         for (int i=1;i<=n;i++)
70             for (int j=1;j<=m;j++)
71                 for (int p=0;p<4;p++)
72                     if (s[k+D][i+dx[p]][j+dy[p]])
73                         add(s[k+D][i+dx[p]][j+dy[p]],s[k][i][j],inf);
74     //for (int i=1;i<=tot;i++) cout<<e[i].s<<' '<<e[i].t<<' '<<e[i].val<<endl;
75     memset(dis,0,sizeof(dis));
76     memset(gap,0,sizeof(gap));
77     gap[0]=num;
78     for (int i=st;i<=ed;i++) last[i]=fir[i];
79     int ans=0;
80     while (dis[st]<num) ans+=dfs(st,inf);
81     printf("%d\n",ans);
82     return 0;
83 }    
cake

 

转载于:https://www.cnblogs.com/zig-zag/archive/2013/05/13/3076563.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值