zoukankan      html  css  js  c++  java
  • Tarjan+树形DP【洛谷P2515】[HAOI2010]软件安装

    【洛谷P2515】[HAOI2010]软件安装

    题目描述

    现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。

    但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。

    我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。

    Tarjan+树形背包。

    Tarjan就是为了恶心人的,注意建边。

    模了第一篇题解大佬的奇淫技巧,对于fa是0的点,可以不去管它,等到缩完点重新建边之后,我们统计每个点的入度,如果该点入度为零,那么说明这个点是森林中一棵树的树根,那么这个时候我们再建立超级源点就可以了。

    不然的话再所点之前建立超级源点真的恶心,深受其害。。。

    至于树形DP,这道题和选课基本上一样,不过我发现了一种更好的有依赖的树形DP的写法,也算是现在才真正学会。

    模板:

    code:

    void dfs(int u){
    	for(int i=w[u];i<=n;i++)f[u][i]=v[u];
        for(int i=head[i];i;i=edge[i].nxt){
    		int v=edge[i].to;
            dfs(v);
            for(int j=m;j>=w[u];j--){
    			for(int k=0;k<=j-w[u];k++){
    				f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]);
                }	
            }
        }
    }
    

    code:

    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    
    const int wx=1017;
    
    inline int read(){
    	int sum=0,f=1; char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0';ch=getchar();}
    	return sum*f;
    }
    
    int num1,num2,n,m,top,tot,col;
    int head1[wx],head2[wx],dfn[wx],low[wx],st[wx];
    int belong[wx],f[wx][wx],tmp[wx];
    int w[wx],v[wx],W[wx],V[wx],fa[wx];
    
    struct node{
    	int nxt,to;
    }edge1[wx*2];
    
    struct e{
    	int nxt,to;
    }edge2[wx*2];
    
    void add1(int from,int to){
    	edge1[++num1].nxt=head1[from];
    	edge1[num1].to=to;
    	head1[from]=num1;
    }
    
    void add2(int from,int to){
    	edge2[++num2].nxt=head2[from];
    	edge2[num2].to=to;
    	head2[from]=num2;
    }
    
    void Tarjan(int u){
    	dfn[u]=low[u]=++tot;
    	st[++top]=u;
    	for(int i=head1[u];i;i=edge1[i].nxt){
    		int v=edge1[i].to;
    		if(!dfn[v]){
    			Tarjan(v);
    			low[u]=min(low[u],low[v]);
    		}
    		else if(!belong[v]){
    			low[u]=min(low[u],dfn[v]);
    		}
    	}
    	if(low[u]==dfn[u]){
    		belong[u]=++col;
    		while(st[top]!=u){
    			belong[st[top]]=col;
    			top--;
    		}
    		top--;
    	}
    }
    
    void CQ(){
    	for(int i=1;i<=n;i++){
    		if(belong[i]!=belong[fa[i]]&&fa[i]){
    			add2(belong[fa[i]],belong[i]);
    			tmp[belong[i]]++;
    		}
    	}
    	for(int i=1;i<=n;i++){
    		W[belong[i]]+=w[i];
    		V[belong[i]]+=v[i];
    	}
    	for(int i=1;i<=col;i++){
    		if(!tmp[i])add2(col+1,i);
    	}
    }
    
    void dfs(int u){
    	for(int i=W[u];i<=m;i++)f[u][i]=V[u];
    	for(int i=head2[u];i;i=edge2[i].nxt){
    		int v=edge2[i].to;
    		dfs(v);
    		for(int j=m;j>=W[u];j--){
    			for(int k=0;k<=j-W[u];k++){
    				f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]);
    			}
    		}
    	}
    }
    
    int main(){
    	n=read();m=read();
    	for(int i=1;i<=n;i++)w[i]=read();
    	for(int i=1;i<=n;i++)v[i]=read();
    	for(int i=1;i<=n;i++){
    		fa[i]=read();
    		if(!fa[i])continue;
    		add1(fa[i],i);
    	}
    	for(int i=1;i<=n;i++)if(!dfn[i])Tarjan(i);
    	CQ();
    	dfs(col+1);
    	printf("%d
    ",f[col+1][m]);
    	return 0;
    }
    
  • 相关阅读:
    理解多线程引用自 http://eagletff.blog.163.com/blog/static/11635092820105158493975/
    Delphi 完全时尚手册之 Visual Style 篇 (界面不错) 转自http://blog.csdn.net/iseekcode/article/details/4733229
    .Delphi下的COM编程 详解Delphi下的COM编程
    TPerlRegEx, delphi 下的正则表达式
    delphi 下多线程 并发操作 数据库(数据库锁) 注意事项
    关于利用其它版本看QQ的是否隐身
    QQ空间的一些操作
    关于自动申请QQ
    千千静听播放时出现杂音,而用其他播放器却没有
    无锡之行
  • 原文地址:https://www.cnblogs.com/wangxiaodai/p/9794297.html
Copyright © 2011-2022 走看看