• [JZOJ4682] 【GDOI2017模拟8.11】生物学家


    题目

    描述

    在这里插入图片描述

    题目大意

    有一个0101序列,可以改变状态,每个状态改变都有固定的代价。
    接下来有些人想要将一些位置改成特定的状态,如果按照他们要求做了就可以得到一些钱,
    否则得不到,有时还要陪钱。
    问最后的钱最多是多少。


    思考历程

    看到这题的第一眼就觉得是一道神题。
    只能想到最恐怖的暴力算法……
    这题肯定不可以DP,那么就想想贪心和网络流。
    觉得这题做法一定是贪心,因为有10410^4这么大,肯定不是网络流啊!
    到最后还是没有想出来。


    正解

    正解早就被我否定了!(真香)
    这题的正解就是网络流啊。
    接下来考虑怎么建图,按照传统套路:
    建立源点SS和汇点TT,若状态为00,则SS向它连一条边,否则它向TT连一条边。
    容量为它们的代价(意味着如果要割掉这条边就要付出这么多代价)
    对于每个人(假设他要求将状态变为0),建一个点(记为xx
    首先SSxx连一条边,容量为收入(如果有赔钱就讲赔钱加上)。接着xx向每个要改变的点连一条容量为无限大的边。
    反之同理。
    然后跑一遍最小割。答案一开始加上所有收入,减去最大流。

    接着分析一下这为什么是对的。
    SSxx连一条边,如果xx的要求不能达到,那么必然有一个要改变的点连向了TT
    所以SSxx连的这一条边会被割掉。
    实际上我们可以看成是xx点对那些点的限制,如果它们oror和为00,那就不用被割,否则SxS o x或那个点到TT之间一定会被割掉一条。

    这题网络流可以过……这是最不可思议的地方……我跑了20ms……


    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 10010
    #define M 2010
    #define INF 2147483647
    int n,m,g;
    int sex[N];
    struct EDGE{
    	int to,c;
    	EDGE *las;
    } e[(N+M*10+M)*2];
    int ne=-1;
    EDGE *last[N+M+2],*cur[N+M+2];
    inline void link(int u,int v,int c){
    	e[++ne]={v,c,last[u]};
    	last[u]=e+ne;
    }
    #define rev(ei) (e+(int(ei-e)^1))
    int cnt,S,T;
    int ans;
    int gap[N+M+2],h[N+M+2];
    bool BZ;
    int dfs(int x,int s){
    	if (x==T)
    		return s;
    	int have=s;
    	for (EDGE *ei=cur[x];ei;ei=ei->las){
    		cur[x]=ei;
    		if (ei->c && h[ei->to]+1==h[x]){
    			int t=dfs(ei->to,min(ei->c,have));
    			ei->c-=t,rev(ei)->c+=t,have-=t;
    			if (!have)
    				return s;
    		}
    	}
    	cur[x]=last[x];
    	if (!--gap[h[x]])
    		BZ=0;
    	h[x]++;
    	gap[h[x]]++;
    	return s-have;
    }
    inline int flow(){
    	int res=0;
    	gap[0]=cnt;
    	memset(h,0,sizeof h);
    	BZ=1;
    	while (BZ)
    		res+=dfs(S,INF);
    	return res; 
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&g);
    	cnt=n;S=++cnt;T=++cnt;
    	for (int i=1;i<=n;++i)
    		scanf("%d",&sex[i]);
    	for (int i=1;i<=n;++i){
    		int w;
    		scanf("%d",&w);
    		if (sex[i]==0)
    			link(S,i,w),link(i,S,0);
    		else
    			link(i,T,w),link(T,i,0);
    	}
    	for (int i=1;i<=m;++i){
    		int ns,val,k;
    		scanf("%d%d%d",&ns,&val,&k);
    		ans+=val;
    		++cnt;
    		if (ns==0){
    			while (k--){
    				int x;
    				scanf("%d",&x);
    				link(cnt,x,INF),link(x,cnt,0);
    			}
    			int gf;
    			scanf("%d",&gf);
    			link(S,cnt,val+gf*g),link(cnt,S,0);
    		}
    		else{
    			while (k--){
    				int x;
    				scanf("%d",&x);
    				link(x,cnt,INF),link(cnt,x,0);
    			}
    			int gf;
    			scanf("%d",&gf);
    			link(cnt,T,val+gf*g),link(T,cnt,0);
    		}
    	}
    	ans=ans-flow();
    	printf("%d
    ",ans);
    	return 0;
    }
    

    总结

    见到一道题,应该要想到DP、贪心、网络流。
    对于网络流,我以前都会分析一下时间复杂度,再看看能不能用网络流。
    可我发现我错了,原来网络流的时间复杂度一直都是玄学。
    因此以后我要改变一下策略:
    见到不会做的题目就用网络流!

  • 相关阅读:
    《Robust Sparse Coding for Face Recognition》
    安装robotframwork 报错Requirement already satisfied
    python -m pip install --upgrade pip 解决升级不成功问题
    pycharm 导入requests库踩坑帖
    新电脑软件安装及环境变量配置
    monkey参数命令
    adb 命令合集
    【转载】解决Sublime编译Python时出现Decode error
    [转载]Python3 接口自动化测试项目实战一(WEB项目)
    python sublime run快捷键设置
  • 原文地址:https://www.cnblogs.com/jz-597/p/11145224.html
Copyright © 2020-2023  润新知