zoukankan      html  css  js  c++  java
  • HAOI2010软件安装(树形背包)

    HAOI2010软件安装(树形背包)

    题意

      有n个物品,每个物品最多会依赖一个物品,但一个物品可以依赖于一个不独立(依赖于其它物品)的物品,且可能有多个物品依赖一个物品,并且依赖关系可能形成一个环。现给你V的资金,问如何分配资金,可以使你的得到的总价值最大,请求出这个总价值。

    解法

      我以前写过对于普通依赖性背包的博客:noip2006金明的预算方案如果对依赖性背包不是很熟悉的同学可以先看一下这道题。

    由于这道题的依赖关系可能形成环,所以我们先用tarjan缩一下点,然后依赖关系图就变成了一个森林,这时候我们再将每一棵树的根节点向0号结点连一条边,表示他们依赖0号结点。这时候我们就得到了一颗依赖关系树。

    那么现在的重点就在如何处理这颗依赖关系树。因为树上每一个节点的决策数都太大了,所以同样的我们考虑求出每一个以i节点为根的子树在任意权值下的最大价值,然后再在i节点利用01背包来合并,一直递归往上。最后的答案就是0号结点所有方案中最优的那一个。

    ps:如果还是看不懂的话,可以参观这里这里我也是在那儿学的。

    代码

    以下的代码是 (O(nv^2)) 的:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <cctype>
    #include <vector>
    #define INF 2139062143
    #define MAX 0x7ffffffffffffff
    #define del(a,b) memset(a,b,sizeof(a))
    using namespace std;
    typedef long long ll;
    template<typename T>
    inline void read(T&x)
    {
        x=0;T k=1;char c=getchar();
        while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}
        while(isdigit(c)){x=x*10+c-'0';c=getchar();}x*=k;
    }
    const int maxn=2500+15;
    int w[maxn],cost[maxn];
    int n,m,ecnt;
    struct Edge{
    	int u,v;
    	Edge(int u,int v):u(u),v(v){}
    };
    vector<Edge> edge;
    vector<int> G[maxn];
    void add_edge(int u,int v) {
    	edge.push_back(Edge(u,v));
    	ecnt=edge.size();
    	G[u].push_back(ecnt-1);
    }
    
    int dfn[maxn],low[maxn],sta[maxn],top,bl[maxn],cnt;
    bool ins[maxn];
    
    void tarjian(int u) {
    	ins[u]=1;sta[++top]=u;
    	dfn[u]=low[u]=++cnt;
    	for(int i=0;i<G[u].size();i++) {
    		Edge e=edge[G[u][i]];
    		int v=e.v;
    		if(!dfn[v]) {
    			tarjian(v);
    			low[u]=min(low[u],low[v]);
    		}
    		else if(ins[v]) low[u]=min(low[u],dfn[v]);
    	}
    	if(low[u]==dfn[u]) {
    		int y;
    		while((y=sta[top--])&&y) {
    			bl[y]=u;
    			ins[y]=0;
    			if(u==y) break;
    			w[u]+=w[y],cost[u]+=cost[y];
    		}
    	}
    }
    int dp[maxn][maxn];
    void dfs(int u) {
    	dp[u][cost[u]]=w[u];
    	for(int i=0;i<G[u].size();i++) {
    		Edge e=edge[G[u][i]];
    		int v=e.v;
    		dfs(v);
    		for(int i=m;i>=cost[u];i--)
    			for(int j=0;j<=i-cost[u];j++)
    			dp[u][i]=max(dp[u][i],dp[u][i-j]+dp[v][j]);
    	}
    }
    bool noroot[maxn];
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=n;i++) read(cost[i]);
    	for(int i=1;i<=n;i++) read(w[i]);
    	for(int i=1;i<=n;i++) {
    		int d;read(d);
    		if(!d) continue;
    		add_edge(d,i);
    	}
    	for(int i=1;i<=n;i++) if(!dfn[i]) tarjian(i);
    	int k=edge.size();
    	for(int i=0;i<=n;i++) G[i].clear();
    	
    	for(int i=0;i<k;i++) {
    		Edge e=edge[i];
    		int f1=bl[e.u],f2=bl[e.v];
    		if(f1==f2) continue;// 新的两个点可能在同一个强连通分量中!! 
    		add_edge(f1,f2);noroot[f2]=1;
    	}
    	
    	for(int i=1;i<=n;i++) if((bl[i]==i)&&(!noroot[i])) add_edge(0,i);
    	dfs(0);
    	int ans=0;
    	for(int i=0;i<=m;i++) 
    		ans=max(ans,dp[0][i]);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    这下面的是参照徐持横的算法做到的 (O(nv)) 的算法:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <cctype>
    #include <vector>
    #define INF 2139062143
    #define MAX 0x7ffffffffffffff
    #define del(a,b) memset(a,b,sizeof(a))
    using namespace std;
    typedef long long ll;
    template<typename T>
    inline void read(T&x)
    {
        x=0;T k=1;char c=getchar();
        while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}
        while(isdigit(c)){x=x*10+c-'0';c=getchar();}x*=k;
    }
    const int maxn=500+15;
    int w[maxn],cost[maxn];
    int n,m,ecnt;
    struct Edge{
    	int u,v;
    	Edge(int u,int v):u(u),v(v){}
    };
    vector<Edge> edge;
    vector<int> G[maxn];
    void add_edge(int u,int v) {
    	edge.push_back(Edge(u,v));
    	ecnt=edge.size();
    	G[u].push_back(ecnt-1);
    }
    
    int dfn[maxn],low[maxn],sta[maxn],top,bl[maxn],cnt;
    bool ins[maxn];
    
    void tarjian(int u) {
    	ins[u]=1;sta[++top]=u;
    	dfn[u]=low[u]=++cnt;
    	for(int i=0;i<G[u].size();i++) {
    		Edge e=edge[G[u][i]];
    		int v=e.v;
    		if(!dfn[v]) {
    			tarjian(v);
    			low[u]=min(low[u],low[v]);
    		}
    		else if(ins[v]) low[u]=min(low[u],dfn[v]);
    	}
    	if(low[u]==dfn[u]) {
    		int y;
    		while((y=sta[top--])&&y) {
    			bl[y]=u;
    			ins[y]=0;
    			if(u==y) break;
    			w[u]+=w[y],cost[u]+=cost[y];
    		}
    	}
    }
    int dp[maxn][maxn];
    void dfs(int u,int M) {
    	if(M<=0) return;
    	for(int i=0;i<G[u].size();i++) {
    		Edge e=edge[G[u][i]];
    		int v=e.v;
    		for(int j=1;j<=M;j++) dp[v][j]=dp[u][j];
    		dfs(v,M-cost[v]);
    		for(int j=cost[v];j<=M;j++) dp[u][j]=max(dp[u][j],dp[v][j-cost[v]]+w[v]);
    	}
    }
    bool noroot[maxn];
    int main()
    {
    	read(n),read(m);
    	for(int i=1;i<=n;i++) read(cost[i]);
    	for(int i=1;i<=n;i++) read(w[i]);
    	for(int i=1;i<=n;i++) {
    		int d;read(d);
    		if(!d) continue;
    		add_edge(d,i);
    	}
    	for(int i=1;i<=n;i++) if(!dfn[i]) tarjian(i);
    	int k=edge.size();
    	for(int i=0;i<=n;i++) G[i].clear();
    	
    	for(int i=0;i<k;i++) {
    		Edge e=edge[i];
    		int f1=bl[e.u],f2=bl[e.v];
    		if(f1==f2) continue;// 新的两个点可能在同一个强连通分量中!! 
    		add_edge(f1,f2);noroot[f2]=1;
    	}
    	
    	for(int i=1;i<=n;i++) if((bl[i]==i)&&(!noroot[i])) add_edge(0,i);
    	dfs(0,m);
    	int ans=0;
    	for(int i=0;i<=m;i++) ans=max(ans,dp[0][i]);
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    sizeof,终极无惑(上)
    send,recv,sendto,recvfrom
    【问卷调查】社团对海大学生成长的影响研究(及部分调查结果)
    Opencv cvCircle函数
    墨菲定律、二八法则、马太效应、手表定理、“不值得”定律、彼得原理、零和游戏、华盛顿合作规律、酒与污水定律、水桶定律、蘑菇管理原理、钱的问题、奥卡姆剃刀等13条是左右人生的金科玉律
    C#操作Excel文件(读取Excel,写入Excel)
    秋风秋雨愁煞人
    Java Applet读写client串口——终极篇
    数据库索引的作用和长处缺点
    EasyARM i.mx28学习笔记——开箱试用总结
  • 原文地址:https://www.cnblogs.com/mrasd/p/9562489.html
Copyright © 2011-2022 走看看