码迷,mamicode.com
首页 > 其他好文 > 详细

Bzoj3894文理分科

时间:2016-12-28 12:05:55      阅读:246      评论:0      收藏:0      [点我收藏+]

标签:sizeof   容量   front   color   its   queue   zoj   turn   include   

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

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

所有割到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\n",ans-MaxFlow());
    return 0;
}

 

Bzoj3894文理分科

标签:sizeof   容量   front   color   its   queue   zoj   turn   include   

原文地址:http://www.cnblogs.com/ihopenot/p/6228593.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!