zoukankan      html  css  js  c++  java
  • 【BZOJ2127】happiness(网络流)

    点此看题面

    大致题意: 每个人只能在文科与理科中选择一种。选择每种科目会带来不同的喜悦值,如果相邻的两位同学选择了同一种科目则会带来额外的喜悦值。求喜悦值总和的最大值。

    网络流

    这道题做法显然是网络流

    但是网络流最难的地方就难在建图。

    建图

    以相邻两点为例,我们可以按照这样的方式建图:

    其中,(a_i)表示(i)选文科的喜悦值(b_i)表示(i)选理科的喜悦值(c_{i,j})表示(i,j)同选文科的喜悦值(d_{i,j})表示(i,j)同选理科的喜悦值

    则可以发现,用喜悦值总和(a_1+b_1+c_{1,2}+d_{1,2})减去图中的任意一个割,都恰好对应某种情况的喜悦值:

    1. (1,2)同选文:割去(1->t,2->t),得(sum-(b_1+frac{d_{1,2}}2)-(b_2+frac{d_{1,2}}2)=a_1+a_2+c_{1,2})
    2. (1,2)同选理:割去(s->1,s->2),得(sum-(a_1+frac{c_{1,2}}2)-(a_2+frac{c_{1,2}}2)=b_1+b_2+d_{1,2})
    3. (1)选文,(2)选理:割去(s->2,1->2,1->t),得(sum-(a_2+frac{c_{1,2}}2)-(frac{c_{1,2}}2+frac{d_{1,2}}2)-(b_1+frac{d_{1,2}}2)=a_1+b_2)
    4. (1)选理,(2)选文:割去(s->1,2->1,2->t),得(sum-(a_1+frac{c_{1,2}}2)-(frac{c_{1,2}}2+frac{d_{1,2}}2)-(b_2+frac{d_{1,2}}2)=a_2+b_1)

    而要使喜悦值最大,就应该用喜悦值总和减去这张图的最小割

    又由于最小割=最大流定理,我们直接求出最大流,然后用喜悦值总和减去即可。

    推广到原图中同理。

    注意这里涉及到除以(2),因此我们在建边时可以将边权都乘(2),最后再除即可。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100
    #define min(x,y) ((x)<(y)?(x):(y))
    #define INF 1e9
    using namespace std;
    int n,m,a[N+5][N+5],b[N+5][N+5];
    class FastIO
    {
        private:
            #define FS 100000
            #define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
            #define tn (x<<3)+(x<<1)
            #define D isdigit(c=tc())
            char c,*A,*B,FI[FS];
        public:
            I FastIO() {A=B=FI;}
            Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
            Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
            #undef D
    }F;
    class Dinic//Dinic跑网络流
    {
        private:
            #define add(x,y,v) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].Cap=v)
            static const int Psz=N*N+2,Lsz=(N*N<<2)+(N*N<<3);int ee,lnk[Psz+5],cur[Psz+5],q[Psz+5],dep[Psz+5];
            struct edge {int to,nxt,Cap;}e[Lsz+5];
            I bool BFS()//BFS找增广路
            {
                RI i,k,H=1,T=1;memset(dep,0,sizeof(dep)),dep[q[1]=s]=1;W(H<=T&&!dep[t])
                    for(i=lnk[k=q[H++]];i;i=e[i].nxt) e[i].Cap&&!dep[e[i].to]&&(dep[q[++T]=e[i].to]=dep[k]+1);
                return dep[t]?(memcpy(cur,lnk,sizeof(lnk)),true):false;
            }
            I int DFS(CI x,RI f)//DFS统计流量
            {
                if(!(x^t)||!f) return f;RI i,t,res=0;
                for(i=cur[x];i;i=e[i].nxt)
                {
                    if(cur[x]=i,(dep[x]+1)^dep[e[i].to]||!(t=DFS(e[i].to,min(f,e[i].Cap)))) continue;
                    if(e[i].Cap-=t,e[((i-1)^1)+1].Cap+=t,res+=t,!(f-=t)) break;
                }return !res&&(dep[x]=-1),res;
            }
        public:
            int s,t;I Dinic() {s=1,t=2;}I int P(CI x,CI y) {return (x-1)*m+y+2;}
            I void AddOneWayEdge(CI x,CI y,CI v) {add(x,y,v),add(y,x,0);}//建单向边
            I void AddTwoWayEdge(CI x,CI y,CI v) {add(x,y,v),add(y,x,v);}//建双向边
            I int MaxFlow() {RI res=0;W(BFS()) res+=DFS(s,INF);return res;}//求最大流
    }D;
    int main()
    {
        RI i,j,x,ans=0;F.read(n,m);
        for(i=1;i<=n;++i) for(j=1;j<=m;++j) F.read(a[i][j]),ans+=a[i][j],a[i][j]<<=1;//读入,更新喜悦值总和,并将其乘2
        for(i=1;i<=n;++i) for(j=1;j<=m;++j) F.read(b[i][j]),ans+=b[i][j],b[i][j]<<=1;//读入,更新喜悦值总和,并将其乘2
        for(i=1;i^n;++i) for(j=1;j<=m;++j) F.read(x),ans+=x,a[i][j]+=x,a[i+1][j]+=x,D.AddTwoWayEdge(D.P(i,j),D.P(i+1,j),x);//读入,更新喜悦值总和和源点流向这两个节点的流量,然后在这两点间建双向边
        for(i=1;i^n;++i) for(j=1;j<=m;++j) F.read(x),ans+=x,b[i][j]+=x,b[i+1][j]+=x,D.AddTwoWayEdge(D.P(i,j),D.P(i+1,j),x);//读入,更新喜悦值总和和这两个节点流向汇点的流量,然后在这两点间建双向边
        for(i=1;i<=n;++i) for(j=1;j^m;++j) F.read(x),ans+=x,a[i][j]+=x,a[i][j+1]+=x,D.AddTwoWayEdge(D.P(i,j),D.P(i,j+1),x);//读入,更新喜悦值总和和源点流向这两个节点的流量,然后在这两点间建双向边
        for(i=1;i<=n;++i) for(j=1;j^m;++j) F.read(x),ans+=x,b[i][j]+=x,b[i][j+1]+=x,D.AddTwoWayEdge(D.P(i,j),D.P(i,j+1),x);//读入,更新喜悦值总和和这两个节点流向汇点的流量,然后在这两点间建双向边
        for(i=1;i<=n;++i) for(j=1;j<=m;++j) D.AddOneWayEdge(D.s,D.P(i,j),a[i][j]),D.AddOneWayEdge(D.P(i,j),D.t,b[i][j]);//建源点流向该节点和该节点流向汇点的单向边
        return printf("%d",ans-(D.MaxFlow()>>1)),0;//输出答案
    }
    
  • 相关阅读:
    linux系统的nobody用户
    java包命名规则
    配置文件解析
    jps参数
    Java数据类型总结
    JSON与JAVA数据的相互转换
    maven中使用net.sf.json-lib
    设计 REST 风格的 MVC 框架
    Java 5种字符串拼接方式性能比较
    spring获取webapplicationcontext,applicationcontext几种方法详解
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2127.html
Copyright © 2011-2022 走看看