zoukankan      html  css  js  c++  java
  • Bzoj3894文理分科

    注意到所有学生分为文理两科实际是把所有学生分为两个集合,如果相邻点全为同一集合有额外贡献

    与最小割模型类似,考虑用最小割来解这道题

    所有割到s的集合的点如果相邻点有割到t集合的就要去掉共有贡献,但是不论他周围有多少与他不同的人,这个贡献只会被扣一次,所以考虑拆点

    可以观察到如果一个点没有选择文科,那么也必然不会有文科共同贡献,理科亦然,所以考虑把选每科贡献与每科共同贡献统一起来

    然后就可以构图了

    设a[x]为x选文科满意度,s[x]为选理科满意度

    Sa[x],Ss[x]分别为选两科的共同贡献

    把每个点拆成三个点ux,x,dx,源向x连容量为a[x]+Sa[x]的边,x向汇连容量为s[x]+Ss[x]的边

    x向ux连容量为Sa[x]的边,dx向x连容量为Ss[x]的边

    ux向所有与x相邻的点连容量为INF的边,所有与x相邻的点向dx连容量为INF的边

    跑最小割即可

    代码:

    #include<bits/stdc++.h>
    #define INF 0x3f3f3f3f
    using namespace std;
    
    inline int _min(int a,int b) {return a<b?a:b;}
    
    #define MAXN 30005
    
    const int S=MAXN-3,T=MAXN-2;
    int n,m,fix,fix2,ans;
    struct Edge{
        int from,to,next,v;
    }e[MAXN*10];
    int cnt=1,head[MAXN];
    inline void insert(int a,int b,int f) {
        e[++cnt].next=head[a];head[a]=cnt;e[cnt].v=f;e[cnt].to=b;e[cnt].from=a;
        e[++cnt].next=head[b];head[b]=cnt;e[cnt].v=0;e[cnt].to=a;e[cnt].from=b;
    }
    
    int mk[105][105],sz,a[105][105],s[105][105],Sa[105][105],Ss[105][105];
    int to[4][2]={1,0,0,1,-1,0,0,-1};
    
    int dis[MAXN],bk[MAXN];queue<int> q;
    bool Bfs() {
        memset(dis,0,sizeof(dis));
        dis[T]=1;q.push(T);
        while(!q.empty()) {
            int now=q.front();q.pop();
            for(int i=head[now];i;i=e[i].next) 
                if(!dis[e[i].to]&&e[i^1].v>0) {
                    dis[e[i].to]=dis[now]+1;
                    q.push(e[i].to);
                }
        }
        return dis[S];
    }
    int Dfs(int v,int a) {
        if(v==T) return a;
        int flow=0,k;
        for(int i=head[v];i;i=e[i].next) {
            if(e[i].v>0&&dis[e[i].to]==dis[v]-1) {
                k=Dfs(e[i].to,_min(a,e[i].v));
                a-=k;flow+=k;e[i].v-=k;e[i^1].v+=k;
                if(a==0) return flow;
            }
        }
        return flow;
    }
    int MaxFlow() {
        int flow=0,h;
        while(Bfs()) {
            while(h=Dfs(S,INF)) 
                flow+=h;
        }
        return flow;
    }
    
    int main() {
        scanf("%d%d",&n,&m);fix=n*m;fix2=n*m*2;
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) {
            mk[i][j]=++sz;
            scanf("%d",&a[i][j]);
        }
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&s[i][j]);
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&Sa[i][j]);
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&Ss[i][j]);
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) {
            ans+=a[i][j]+s[i][j]+Sa[i][j]+Ss[i][j];
            insert(S,mk[i][j],a[i][j]+Sa[i][j]);
            insert(mk[i][j],T,s[i][j]+Ss[i][j]);
        }
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) {
            insert(mk[i][j],mk[i][j]+fix,Sa[i][j]);
            insert(mk[i][j]+fix2,mk[i][j],Ss[i][j]);
            for(int k=0;k<4;k++) {
                if(!mk[i+to[k][0]][j+to[k][1]]) continue;
                insert(mk[i][j]+fix,mk[i+to[k][0]][j+to[k][1]],INF);
                insert(mk[i+to[k][0]][j+to[k][1]],mk[i][j]+fix2,INF);
            }
        }
        printf("%d
    ",ans-MaxFlow());
        return 0;
    }
  • 相关阅读:
    MySQL中内存分为全局内存和线程内存
    mysql 线程级别的缓冲区
    MySQL Handler变量解析
    HANDLER命令与实现
    MySQL Spatial Extensions 地理信息
    C++中临时对象的学习笔记
    HDC与CDC相互转换
    B树
    基于R-Tree的最近邻查询
    地图发布的几种服务的区别
  • 原文地址:https://www.cnblogs.com/ihopenot/p/6228593.html
Copyright © 2011-2022 走看看