• 聚变反应炉


    读完题过后,我们发现n大的数据c只有0或者1的情况,很显然是要我们分类讨论,(如果不分类只有80分,会T)

    我们换一个角度来看这道题的答案

    他让我们求出按顺序点亮一些点,然后总共的花费是多少

    我们可以视作我们已经点了所有点,然后按顺序点一些点,最后最多能减少多少的花费

    case 1:(c只有0或者1)

    很显然是把1的点优先点亮,然后再点0的点

    模拟一下即可

    for(i=1;i<=n;++i)
    if(c[i]){   //如果是1
    	ans=ans+d[i];    //加上花费
    	d[i]=0;        //花费搞成0
    	for(j=head[i];!!j;j=nex[j])
    	--d[to[j]];     //相邻的点-1
    }
    for(i=1;i<=n;++i)
    if(d[i]>0)ans=ans+d[i];//点亮为0的点
    printf("%d
    ",ans);
    

    case 2:

    我们假设要求x

    那么我们可以有两种情况

    1.我们先点亮fa[x]再点亮x

    2.我们先点亮x再点亮fa[x]

    那么我们就设f[x][0/1] 表示情况2/1

    那么我们考虑如何求出f[x][0/1]

    我们在设tmp[i] 表示x收到了来自儿子的i能量,把子树搞好的最小价值

    就可以轻松求出f[x][0/1]

    那么现在的问题是求出tmp[i]

    很显然我们可以用滚动数组+背包求出来

    (tmp[now][i]=min(f[y][0]+tmp[now space xor space 1][i-c[y]],f[y][1]+tmp[now space xor space 1][i]))

    然后就完了

    inline void dfs(int x,int ne){
    	int i,sum(0),y;
    	for(i=head[x];!!i;i=nex[i]){
    		y=to[i];
    		if(y==ne)continue;
    		dfs(y,x);
    		sum=sum+c[y];
    	}
    	int now(0),j;
    	memset(tmp[now],63,sizeof(tmp[now]));
    	tmp[now][0]=0;
    	for(i=head[x];!!i;i=nex[i]){
    		y=to[i];
    		if(y==ne)continue;
    		now^=1;
    		memset(tmp[now],63,sizeof(tmp[now]));
    		for(j=sum;j>=0;--j){
    			if(j+c[y]<=sum)tmp[now][j+c[y]]=min(tmp[now][j+c[y]],tmp[now^1][j]+f[y][0]);
    			tmp[now][j]=min(tmp[now][j],tmp[now^1][j]+f[y][1]);
    		}
    	}
    	memset(f[x],63,sizeof(f[x]));
    	for(i=0;i<=sum;++i)
    	f[x][0]=min(f[x][0],max(tmp[now][i],tmp[now][i]+d[x]-i)),
    	f[x][1]=min(f[x][1],max(tmp[now][i],tmp[now][i]+d[x]-i-c[ne]));
    }
    

    代码

    #include<bits/stdc++.h>
    #define N 100011
    #define M 200011
    #define MAXSUM 501011
    using namespace std;
    int head[N],nex[M],to[M],tot;
    inline void build(int x,int y){
    	to[++tot]=y;
    	nex[tot]=head[x];
    	head[x]=tot;
    }
    int d[N],c[N];
    int tmp[2][MAXSUM],n,f[N][2];
    inline void dfs(int x,int ne){
    	int i,sum(0),y;
    	for(i=head[x];!!i;i=nex[i]){
    		y=to[i];
    		if(y==ne)continue;
    		dfs(y,x);
    		sum=sum+c[y];
    	}
    	int now(0),j;
    	memset(tmp[now],63,sizeof(tmp[now]));
    	tmp[now][0]=0;
    	for(i=head[x];!!i;i=nex[i]){
    		y=to[i];
    		if(y==ne)continue;
    		now^=1;
    		memset(tmp[now],63,sizeof(tmp[now]));
    		for(j=sum;j>=0;--j){
    			if(j+c[y]<=sum)tmp[now][j+c[y]]=min(tmp[now][j+c[y]],tmp[now^1][j]+f[y][0]);
    			tmp[now][j]=min(tmp[now][j],tmp[now^1][j]+f[y][1]);
    		}
    	}
    	memset(f[x],63,sizeof(f[x]));
    	for(i=0;i<=sum;++i)
    	f[x][0]=min(f[x][0],max(tmp[now][i],tmp[now][i]+d[x]-i)),
    	f[x][1]=min(f[x][1],max(tmp[now][i],tmp[now][i]+d[x]-i-c[ne]));
    }
    int main( ){
    	scanf("%d",&n);
    	int i,j,k;
    	int x,y,z,maxc(0);
    	for(i=1;i<=n;++i)
    	scanf("%d",&d[i]);
    	for(i=1;i<=n;++i){
    		scanf("%d",&c[i]);
    		maxc=max(maxc,c[i]);
    	}
    	for(i=1;i<n;++i){
    		scanf("%d%d",&x,&y);
    		build(x,y);
    		build(y,x);
    	}
    	int ans(0);
    	if(maxc<=1){
    		for(i=1;i<=n;++i)
    		if(c[i]){
    			ans=ans+d[i];
    			d[i]=0;
    			for(j=head[i];!!j;j=nex[j])
    			--d[to[j]];
    		}
    		for(i=1;i<=n;++i)
    		if(d[i]>0)ans=ans+d[i];
    		printf("%d
    ",ans);
    		return 0;
    	}
    	dfs(1,0);
    	printf("%d
    ",f[1][0]);
    }
    
  • 相关阅读:
    雅虎公司C#笔试题
    DES
    从Socket看Visual C#.Net网络程序开发
    进一步接触C#委托与事件
    学习C#消息:循序渐进
    解惑答疑:C#委托和事件
    利用Visual C#实现Windows管道技术1
    使用命名管道通过网络在进程之间进行通信
    C#体验编程技术 类与对象
    学习C#实现HTTP协议:多线程文件传输
  • 原文地址:https://www.cnblogs.com/the-Blog-of-Mikasa/p/14006404.html
Copyright © 2020-2023  润新知