zoukankan      html  css  js  c++  java
  • 【HDU4276】The Ghost Blows Light

    题目大意:给定一棵有根树,1 号节点为根节点,点有点权,边有边权,初始给定一个价值,每经过一条边都会减少该价值,每经过一个点都会增加相应的答案贡献值,求如何在给定价值的情况下最大化答案贡献,并要求最后在 N 号节点停留,若无法停留,则输出相应字符串。

    题解:
    首先,不考虑要求在 N 号节点停留的限制,发现就是一个裸的树上背包问题。但是现在多了一个限制条件,我们假设一定可以从根节点走到 N 号节点。

    引理:最有情况下,从根节点到 N 号节点的树链中的每一条边经过且仅经过一次。
    证明:若先经过树链上的边 e ,再通过 e 返回,再经过其他非树链边之后,再次返回 e,得到的一条路径 P。构造一条路径 P‘,使得在未经过 e 时,先经过 P’ 中的对应非树边,再经过 e ,得到路径 P。发现 P 总是比 P‘ 小 2e 的代价,证毕。

    根据引理,又根据从 1 到 N 的路径必须经过的性质,采用将这条树链压缩成一个点,即:将这条路径上的点压缩成一个点,再在新的树上进行树形dp操作,得到的最优解即是全局最优解。对于压缩路径的等价操作是:记录这条必经树链的每条边,将边权全部置为 0 即可。

    代码如下

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=105;
    const int maxt=505;
    
    int n,t,a[maxn];
    struct node{int nxt,to,w;}e[maxn<<1];
    int tot=1,head[maxn];
    inline void add_edge(int from,int to,int w){
    	e[++tot]=node{head[from],to,w},head[from]=tot;
    }
    int d[maxn],pre[maxn],f[maxn][maxt];
    
    void dfs(int u,int fa){
    	for(int i=head[u];i;i=e[i].nxt){
    		int v=e[i].to,w=e[i].w*2;
    		if(v==fa)continue;
    		dfs(v,u);
    		for(int j=t;j>=0;j--)
    			for(int k=0;k<=j-w;k++)
    				f[u][j]=max(f[u][j],f[u][j-k-w]+f[v][k]);
    	}
    	for(int j=0;j<=t;j++)f[u][j]+=a[u];
    }
    void getdis(int u,int fa){
    	for(int i=head[u];i;i=e[i].nxt){
    		int v=e[i].to,w=e[i].w;
    		if(v==fa)continue;
    		pre[v]=i,d[v]=d[u]+w;
    		getdis(v,u);
    	}
    }
    void clear(){
    	int now=n;
    	while(now!=1){
    		int id=pre[now];
    		e[id].w=e[id^1].w=0;
    		now=e[id^1].to;
    	}
    }
    void read_and_parse(){
    	for(int i=1;i<n;i++){
    		int x,y,z;
    		scanf("%d%d%d",&x,&y,&z);
    		add_edge(x,y,z),add_edge(y,x,z);
    	}
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    }
    void solve(){
    	getdis(1,0);
    	if(d[n]>t)return (void)puts("Human beings die in pursuit of wealth, and birds die in pursuit of food!");
    	clear();
    	t-=d[n];
    	dfs(1,0);
    	printf("%d
    ",f[1][t]);
    }
    void init(){
    	memset(head,0,sizeof(head)),tot=1;
    	memset(pre,0,sizeof(pre));
    	memset(d,0,sizeof(d));
    	memset(f,0,sizeof(f));
    }
    int main(){
    	while(scanf("%d%d",&n,&t)!=EOF){
    		init();
    		read_and_parse();
    		solve();
    	}
    	return 0;
    } 
    
  • 相关阅读:
    词法分析程序
    0909关于编译原理
    深度学习中图像检测的评价标准
    【 记忆网络 1 】 Memory Network
    ssm又乱码
    百度地图标注没了
    Fragment与Activity交互(使用Handler)
    在android里用ExpandableListView实现二层和三层列表
    java中outer的使用
    android中使用Http下载文件并保存到本地SD卡
  • 原文地址:https://www.cnblogs.com/wzj-xhjbk/p/10933398.html
Copyright © 2011-2022 走看看