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

[CF331E] Biologist

时间:2020-01-26 22:11:26      阅读:93      评论:0      收藏:0      [点我收藏+]

标签:ret   return   insert   const   cpp   for   要求   stream   std   

题目大意

有一个长度为 \(n\) 的 01 串,将第 \(i\) 个位置变为另外一个数字的代价是\(v_i\) 。 有 \(m\) 个要求 每个要求的形式是 首先确定若干位置都要是 \(0\) 或者 \(1\)
然后给定这 \(k\) 个位置,如果些位置上都满足要求 那么就可以得到 \(W_k\)? 元 某些要求如果失败了还要倒着给\(g\) 元。问最终能够得到的最大利润

输入格式: 第一行是 \(n,m,g\)
第二行是初始的 01 串 第三行是 \(V_i\)?
接下来 \(m\) 行 第一个数字表示这个集合都要是 0 还是 1
第二个数字 \(W_i\) ? 表示利润,接下来 \(k_i\)? 表示这个集合中有 \(k\) 个位置 接下来是这 \(k\) 个位置, 最后还有一个 0/1 ,如果是 1 ,表示如果失败了还要倒着给 \(g\) 元。

解析

一道非常满足最小割模型的题目。对于每个点,如果最开始是0,我们从原点向它连边,边权为\(v_i\),表示这个点不为0的代价。同理,对于一个为1的点,将它向汇点连边,边权为\(v_i\),表示该点不为1的代价。

对于每一组要求,如果是要全为0,就从原点向这个要求对应的点连边,边权为这个要求不满足的代价(包括\(w_i\)以及是否有额外代价)。然后从要求对应的边向要求中的点连边,边权为无穷大。如果要求全为1,就从这个要求对应的点向汇点连边,边权为这个要求不满足的代价。然后从要求中的点向这个点连边,边权无穷大。用总代价减去最小割即为答案。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define N 20002
#define M 2000002
using namespace std;
const int inf=1<<30;
int head[N],ver[M],nxt[M],cap[M],l;
int n,m,g,s,t,i,j,a[N],v[N],dis[N],ans;
int read()
{
    char c=getchar();
    int w=0;
    while(c<'0'||c>'9') c=getchar();
    while(c<='9'&&c>='0'){
        w=w*10+c-'0';
        c=getchar();
    }
    return w;
}
void insert(int x,int y,int z)
{
    ver[l]=y;
    cap[l]=z;
    nxt[l]=head[x];
    head[x]=l;
    l++;
    ver[l]=x;
    nxt[l]=head[y];
    head[y]=l;
    l++;
}
bool bfs()
{
    queue<int> q;
    memset(dis,-1,sizeof(dis));
    q.push(s);
    dis[s]=0;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=head[x];i!=-1;i=nxt[i]){
            int y=ver[i];
            if(dis[y]==-1&&cap[i]>0){
                dis[y]=dis[x]+1;
                q.push(y);
            }
        }
    }
    return (dis[t]>0);
}
int dfs(int x,int flow)
{
    if(x==t||flow==0) return flow;
    int ans=0;
    for(int i=head[x];i!=-1;i=nxt[i]){
        int y=ver[i];
        if(dis[y]==dis[x]+1&&cap[i]>0){
            int a=dfs(y,min(flow,cap[i]));
            ans+=a;
            flow-=a;
            cap[i]-=a;
            cap[i^1]+=a;
        }
        if(flow==0) break;
    }
    if(flow) dis[x]=-1;
    return ans;
}
int Dinic()
{
    int ans=0;
    while(bfs()) ans+=dfs(s,inf);
    return ans;
}
int main()
{
    memset(head,-1,sizeof(head));
    n=read();m=read();g=read();
    t=n+m+1;
    for(i=1;i<=n;i++) a[i]=read();
    for(i=1;i<=n;i++) v[i]=read();
    for(i=1;i<=n;i++){
        if(a[i]==0) insert(s,i,v[i]);
        else insert(i,t,v[i]);
    }
    for(i=1;i<=m;i++){
        int op=read(),w=read(),k=read();
        if(op==0){
            for(j=1;j<=k;j++){
                int x=read();
                insert(n+i,x,inf);
            }
            int flag=read();
            if(flag) insert(s,n+i,w+g);
            else insert(s,n+i,w);
            ans+=w;
        }
        else{
            for(j=1;j<=k;j++){
                int x=read();
                insert(x,n+i,inf);
            }
            int flag=read();
            if(flag) insert(n+i,t,w+g);
            else insert(n+i,t,w);
            ans+=w;
        }
    }
    printf("%d\n",ans-Dinic());
    return 0;
}

[CF331E] Biologist

标签:ret   return   insert   const   cpp   for   要求   stream   std   

原文地址:https://www.cnblogs.com/LSlzf/p/12234812.html

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