2020CCPC秦皇岛 k Kingdom’s Power

17 篇文章 0 订阅

在这里插入图片描述
题意:有无数个军队可以从树根1出发,每花费一个点可以让军队走一步,问遍历完整个树的最小花费。
思路:先记录每个节点的高度,用节点根据它子树的高度的高度来排序,然后节点的高度为子树的最高高度+1,然后类似树形dp,从上往下递归h和与从下往上走的值取min。最后把所以的根节点的值加起来。

#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=1000010;
typedef long long ll;
int n;
vector<pair<int,int>>v[N];
int geth(int u)
{
	int hh=0;
	if(!v[u].size())	return 1;
	for(int i=0;i<v[u].size();i++)
	{
		v[u][i].first=geth(v[u][i].second);
	}
	sort(v[u].begin(),v[u].end());
	return v[u].back().first+1;
}
int val[N];
int dfs(int u,int h,int V)
{
	val[u]=V;
	if(!v[u].size())	
	return 1;
	int t=V;
	for(int i=0;i<v[u].size();i++)
	{
		t=min(h,dfs(v[u][i].second,h+1,t+1));
	}
	return t+1;
}
int main()
{
	int t;
	scanf("%d",&t);
	int k=0;
	while(t--)
	{	
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			v[i].clear();
		for(int i=1;i<=n;i++)
			val[i]=0;
		for(int i=2;i<=n;i++)
		{
			int fa;
			scanf("%d",&fa);
			v[fa].push_back({0,i});
		}
		geth(1);
		dfs(1,0,0);
		ll ans=0;
		for(int i=1;i<=n;i++)
			if(!v[i].size())
			{
				ans+=val[i];
			//	cout<<val[i]<<endl;
			}
		printf("Case #%d: ",++k);
		printf("%lld\n",ans);
	}
}

树形dp做法
f [ u ] [ 0 / 1 ] f[u][0/1] f[u][0/1]代表以u这个节点为根,1代表不从根发配军队所有军队最后回到u节点,0代表从根发配军队至少存在一个军队在叶子结点的最小代价,显然最后答案的集合肯定存在一个军队驻扎在叶子结点。
dp[u][1]显然是定值,dp[u][0]在转移的时候只需要在总的转移里确定一个儿子j停留在叶子即可。 最后答案是dp[1][0]。

	
	#include<cstdlib>
	#include<string>
	#include<cstring>
	#include<cstdio>
	#include<algorithm>
	#include<climits>
	#include<cmath>
	#include<cctype>
	#include<stack>
	#include<queue>
	#include<list>
	#include<vector>
	#include<iostream>
	#include<set>
	#include<map>
	#include<sstream>
	#include<unordered_map>
	#include<unordered_set>
	using namespace std;
	const int N=1000010;
	vector<int>v[N];
	int mxdep[N];
	int sz[N];
	int dep[N];
	int dep2[N];
	void dfs(int u,int fa)
	{
		sz[u]=1;
		dep[u]=dep[fa]+1;
		mxdep[u]=dep[u];
		for(auto j:v[u])
		{
			
			dfs(j,u);
			sz[u]+=sz[j];
			mxdep[u]=max(mxdep[u],mxdep[j]);
		}
	}
	int dp[N][2];
	long long res=0;
	void dfs2(int u,int fa)
	{
	//	dep[u]=dep[fa]+1;
	//	cout<<u<<" "<<v[u].size()<<endl;
		if(!v[u].size())
		{
			dp[u][0]=dep[u];
			dp[u][1]=0;
			// cout<<u<<" "<<dep[u]<<endl;
			return ;
		}
		
		int sum=0;
		dp[u][1]=0;
		for(auto j:v[u])
		{
			dfs2(j,u);
			dp[u][1]+=2+dp[j][1];
			sum+=min(dp[j][1]+2,dp[j][0]);
		}
		for(auto j:v[u])
		{
			dp[u][0]=min(dp[u][0],sum-min(dp[j][1]+2,dp[j][0]) + dp[j][0]);
		}
	}
	int main(){
		
		int  t;
		scanf("%d",&t);
		int T=0;
		while(t--) 
		{
			res=0;
			int n;
			scanf("%d",&n);
			for(int i=1;i<=n;i++)	dp[i][0]=dp[i][1]=0x3f3f3f3f;
			for(int i=1;i<=n;i++)
			{
				if(i>=2){
				int x;
				scanf("%d",&x);
				v[x].push_back(i);
				}
			}
			dep[0]=-1;
			dfs(1,0);
			dfs2(1,0);
			// cout<<dp[4][0]<<endl;
			for(int i=1;i<=n;i++)	v[i].clear();
			printf("Case #%d: %d\n",++T,dp[1][0]);
			
		}
	}
	/*
	100
	15
	1 2 3 4 5 5 7 7 14 7 11 12 6 14 
	*/
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值