• BZOJ 2039 人员雇佣 (最小割)


    题面:BZOJ传送门

    网络流的题真神仙= =

    大致分为三种情况

    • 选某个人$i$,收益减少$a_{i}$
    • 选了$i$选了$j$,收益增加$2e_{ij}$
    • 选了$i$不选$j$,收益减少$e_{ij}$

    收益问题用最小割的常用套路,实际收益$=$可能的收益总和$sum-$最小割

    考虑最小割如何建图

    源点$S$向$i$连流量为$sum_{j}e_{ij}$的边,$i,j$之间连流量为$2e_{ij}$的双向边,$i$向汇点$T$连流量为$a_{i}$的边

    对于两个人$i$和$j$,把上述三种情况带入

    选了i选了j

    $i,j$和$T$之间的边被割断,流量为$a_{i}$和$a_{j}$,减掉即可,$2e_{ij}$这部分收益已经在$sum$里了,不需要管

    选了i不选j

    $i$和$T$之间的边被割断,$S$和$j$之间的边被割断。

    此时,连接在$i,j$之间流量为$2e_{ij}$的边就要发挥作用了

    如果想继续保持现在的割边状态,会有一条流量为$2e_{ij}$的流$S$->$i$->$j$->$T$

    其中$e_{ji}$($j$对这种情况的贡献)已经在割$S->j$时从$sum$里去掉了

    我们不仅要去掉$sum$里的$e_{ij}$($i$的贡献)。还要去掉一份e_{ij},因为题目要求选$i$不选$j$会带来额外的负收益$e_{ij}$,一共是$2e_{ij}$

    所以$i,j$之间的边流量是$2e_{ij}$

    不选i不选j

    收益减少$2e_{ij}$,分别在割掉$S$和$i$的边以及$S$和$j$的边被去掉了

    这道题目启示我们,化边为点能解决很多问题,但较为暴力

    在不知道如何维护“割”这样一个结构时,可以考虑问题的本质,对某些点的某些量进行求和

    另外这题爆int

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define N1 1010
     6 #define M1 1010000
     7 #define ll long long
     8 using namespace std;
     9 //re
    10 
    11 const ll inf=0x3f3f3f3f3f3f;
    12 int gint()
    13 {
    14     int ret=0,fh=1; char c=getchar();
    15     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
    16     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
    17     return ret*fh;
    18 }
    19 struct Edge{
    20 int head[N1],to[M1<<1],nxt[M1<<1],cte; ll flow[M1<<1];
    21 void ae(int u,int v,ll f)
    22 {
    23     cte++; to[cte]=v; nxt[cte]=head[u];
    24     head[u]=cte; flow[cte]=f;
    25 }
    26 }e;
    27 
    28 int n,m,hd,tl,S,T;
    29 int dep[N1],cur[N1],que[M1];
    30 int bfs()
    31 {
    32     int x,j,v;
    33     memset(dep,-1,(T+1)<<2); memcpy(cur,e.head,(T+1)<<2);
    34     hd=1,tl=0; que[++tl]=S; dep[S]=0;
    35     while(hd<=tl)
    36     {
    37         x=que[hd++];
    38         for(j=e.head[x];j;j=e.nxt[j])
    39         {
    40             v=e.to[j];
    41             if( e.flow[j]>0 && dep[v]==-1 )
    42                 dep[v]=dep[x]+1, que[++tl]=v;
    43         }
    44     }
    45     return dep[T]!=-1;
    46 }
    47 ll dfs(int x,ll limit)
    48 {
    49     int j,v;ll flow,ans=0;
    50     if(x==T||!limit) return limit;
    51     for(j=cur[x];j;j=e.nxt[j])
    52     {
    53         v=e.to[j]; cur[x]=j;
    54         if( dep[v]==dep[x]+1 && (flow=dfs(v,min(e.flow[j],limit))) )
    55         {
    56             e.flow[j]-=flow; limit-=flow;
    57             e.flow[j^1]+=flow; ans+=flow;
    58             if(!limit) break;
    59         }
    60     }
    61     return ans;
    62 }
    63 ll Dinic()
    64 {
    65     ll ans=0;
    66     while(bfs())
    67         ans+=dfs(S,inf);
    68     return ans;
    69 }
    70 
    71 
    72 int a[N1],E[N1][N1]; ll sum[N1];
    73 int main()
    74 {
    75     scanf("%d",&n); S=0; T=n+1; e.cte=1;
    76     int i,j,s; ll k,tot=0,ans=0; 
    77     for(i=1;i<=n;i++) a[i]=gint();
    78     for(i=1;i<=n;i++) for(j=1;j<=n;j++)
    79         E[i][j]=gint(), sum[i]+=E[i][j], tot+=E[i][j];
    80     for(i=1;i<=n;i++) for(j=1;j<=n;j++)
    81     {
    82         if(i==j) continue;
    83         e.ae(i,j,2*E[i][j]); e.ae(j,i,0);
    84     }
    85     for(i=1;i<=n;i++) e.ae(S,i,sum[i]), e.ae(i,S,0);
    86     for(i=1;i<=n;i++) e.ae(i,T,a[i]), e.ae(T,i,a[i]);
    87     ans=Dinic();
    88     printf("%lld
    ",tot-ans);
    89     return 0;
    90 }
  • 相关阅读:
    Android编译选项eng、user、userdebug的区别
    Linux 内核编码规范
    PLM之EVT、DVT、PVT、MP
    fastboot刷机的一般方法
    Android手机拨号测试项
    使用Genymotion安装APK出现错误INSTALL_FAILED_CPU_ABI_INCOMPATIBLE的解决办法
    三星手机列表拖动时出现诡异背景色的问题
    分享android ADT百度云盘下载地址
    关于互联网思维
    分享Nginx在Windows下的管理命令(bat文件)
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10350672.html
Copyright © 2020-2023  润新知