zoukankan      html  css  js  c++  java
  • P2762 太空飞行计划问题

    传送门

    经典的最大权闭合子图问题

    实验有正的价值,仪器的价值为负

    为了实验我们必须选择相应的仪器

    所以从 S 连向实验,边权为实验的价值

    实验与相应仪器之间连边,边权为 INF

    仪器连向 T 边权为仪器的价格

    解释:

    首先最大权闭合子图就是要求在一个图中求出一个联通子图

    该子图没有出边能到达非子图的其他点(闭合)

    且权值最大

    根据我们刚才的建图方式,S 到正权值的点连边,边权为点权,负权值点连向 T,边权为点权绝对值

    点之间根据依赖关系连边,边权INF

    如果我们搞一个最小割,考虑最小割的实际意义

    显然只会割从 S 到实验的边和从仪器到 T 的边

    如果割的是从 S 到实验的边则相当于我们放弃了此实验从而可以不选择此实验需要的仪器,要扣去实验的价值

    如果割的是从仪器到 T 的边则向当于我们为了某些实验而选择了此仪器,要扣去仪器的价值

    那么最大收益就是实验总价值(包括没进行的实验)减去最小割,显然减最小割是最优的方案,因为如果还有流量那么说明有实验还有仪器没得到

    那么还要继续考虑放弃实验或购买仪器

    最后求出最大收益还不够,还要具体到实验和仪器

    发现数据其实不大

    所以枚举仪器,每次把此仪器的价格调成 0,如果跑完最小割发现最终收益恰好多了此仪器的价格则说明此仪器有被选择

    然后根据选择的仪器就可以知道哪些实验有被进行

    因为读入十分毒瘤,所以要稍微注意一下

    我读入用快读加了个特判就解决了

    具体看代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    using namespace std;
    typedef long long ll;
    bool flag;//flag用来特判是否换行
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        if(ch=='
    ') flag=1;//如果有换行flag=1
        return x*f;
    }
    const int N=4e5+7,INF=1e9+7;
    int fir[N],from[N<<1],to[N<<1],val[N<<1],cntt,Fir[N];
    inline void add(int a,int b,int c)
    {
        from[++cntt]=fir[a]; fir[a]=cntt;
        to[cntt]=b; val[cntt]=c;
        from[++cntt]=fir[b]; fir[b]=cntt;
        to[cntt]=a; val[cntt]=0;
    }
    int n,m,S,T;
    int ans;
    int dep[N];
    queue <int> q;
    int BFS()
    {
        for(int i=1;i<=T;i++) dep[i]=0;
        q.push(S); dep[S]=1;
        while(!q.empty())
        {
            int x=q.front(); q.pop();
            for(int i=fir[x];i;i=from[i])
            {
                int &v=to[i]; if(dep[v]||!val[i]) continue;
                q.push(v); dep[v]=dep[x]+1;
            }
        }
        for(int i=1;i<=T;i++) Fir[i]=fir[i];
        return dep[T];
    }
    int dfs(int x,int mif)
    {
        if(x==T||!mif) return mif;
        int fl=0,res=0;
        for(int i=Fir[x];i;i=from[i])
        {
            Fir[x]=i; int &v=to[i]; if(dep[v]!=dep[x]+1) continue;
            if( res=dfs(v,min(mif,val[i])) )
            {
                fl+=res; mif-=res;
                val[i]-=res; val[i^1]+=res;
                if(!mif) break;
            }
        }
        return fl;
    }
    int Val[N],Cst[N];//Val是实验价值,Cst是仪器花费
    vector <int> ned[N];//存实验需要的仪器编号
    int solve(int P)//枚举仪器求最大收益
    {
        memset(fir,0,sizeof(fir)); cntt=1;//记得初始化
        int res=0;
        for(int i=1;i<=n;i++)
        {
            add(S,i,Val[i]);
            res+=Val[i];
            int len=ned[i].size();
            for(int j=0;j<len;j++)
                add(i,n+ned[i][j],INF);
        }
        for(int i=1;i<=m;i++)
            if(i!=P) add(n+i,T,Cst[i]);//建图不解释
        while(BFS()) res-=dfs(S,INF);
        return res;//返回最大收益
    }
    bool vis[N];//判断仪器是否购买
    int main()
    {
        int a;
        n=read(),m=read();
        S=n+m+1; T=n+m+2;
        for(int i=1;i<=n;i++)
        {
            flag=0; Val[i]=read();
            while(!flag)//判断是否换行
                ned[i].push_back(read());
        }
        for(int i=1;i<=m;i++) Cst[i]=read();
        ans=solve(0);//先求一波最大收益
        for(int i=1;i<=m;i++)//枚举仪器
        {
            int p=solve(i);
            if(p-ans==Cst[i]) vis[i]=1;//注意要恰好等于
        }
        for(int i=1;i<=n;i++)//然后根据仪器求出实验是否进行
        {
            int P=1,len=ned[i].size();
            for(int j=0;j<len;j++) P&=vis[ned[i][j]];
            if(P) printf("%d ",i);
        }
        printf("
    ");
        for(int i=1;i<=m;i++) if(vis[i]) printf("%d ",i);
        printf("
    ");
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    qt5--创建控件的两种方式
    qt5-编码转换
    C++qt助手assistant
    C++opencv绘制几何图形
    C++opencv创建图像
    【全球软件大会】华为前端工程师分享:华为云官网的智能化实践
    图解 Redis丨这就是 RDB 快照,能记录实际数据的
    云小课 | 玩转HiLens Studio之快速订购HiLens Studio版本
    带你认识4种设计模式:代理模式、装饰模式、外观模式和享元模式
    线性表、顺序表和链表,你还分不清?
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10191245.html
Copyright © 2011-2022 走看看