jzoj p1306 河流

题目描述

几乎整个Byteland王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海。这条大河的入海口处有一个村庄——名叫Bytetown。

在Byteland国,有n个伐木的村庄,这些村庄都座落在河边。目前在Bytetown,有一个巨大的伐木场,它处理着全国砍下的所有木料。木料被砍下后,顺着河流而被运到Bytetown的伐木场。Byteland的国王决定,为了减少运输木料的费用,再额外地建造k个伐木场。这k个伐木场将被建在其他村庄里。这些伐木场建造后,木料就不用都被送到Bytetown了,它们可以在运输过程中第一个碰到的新伐木场被处理。显然,如果伐木场座落的那个村子就不用再付运送木料的费用了。它们可以直接被本村的伐木场处理。

注:所有的河流都不会分叉,形成一棵树,根结点是Bytetown。

国王的大臣计算出了每个村子每年要产多少木料,你的任务是决定在哪些村子建设伐木场能获得最小的运费。其中运费的计算方法为:每一吨木料每千米1分钱。

输入输出格式

输入格式:

第一行包括两个数 n(2≤n≤100),k(1≤k≤50,且 k≤n)。n为村庄数,k为要建的伐木场的数目。除了Bytetown外,每个村子依次被命名为1,2,3……n,Bytetown被命名为0。

接下来n行,每行3个整数:

wi——每年i村子产的木料的块数(0≤wi≤10000)

vi——离i村子下游最近的村子(即i村子的父结点)(0≤vi≤n)

di——vi到i的距离(千米)。(1≤di≤10000)

保证每年所有的木料流到bytetown的运费不超过2,000,000,000分

50%的数据中n不超过20。

输出格式:

输出最小花费,单位为分。

输入输出样例

输入样例#1:
4 2
1 0 1
1 1 10
10 2 5
1 2 3
输出样例#1:
4
ps;题面沾了落谷的;



一个不错的树形dp题
由题意来看 这是一颗树
我们可以将节点和可支配伐木场数作为状态,再对每一节点的子节点分配伐木场数即可;
但这样显然是不行的,先不谈时间的问题,单是“对每一节点分配伐木场数”这一要求就难以利用代码实现(焫鷄认为)
所以我们秉承着复杂问题简单化的思想 把他化简为一个左儿子右兄弟的二叉树,这样我们对于每一个节点,只需分配两个子节点即可;
这样提交上去,只能拿到30分,时间大大超时。
那我们就应该考虑一下该如何优化了。
首先对于dp问题我们必须是要用到记忆化搜索的(或是状态转移方程),但是如果单单的记录节点和伐木数这两种状态也是很复杂的,因为对于一个点来说,他拥有的伐木数确定并不意味着这个点是唯一的,因为之前的伐木数分配我们并不清楚,所以我们决定再加上一个状态,即之前的伐木数分配。但其实我们并不需要记录之前所有的分配,只需记录与该点有关的即可,也就是离该点最近的那个伐木场的位置,有了这三个状态,程序的效率就会高上许多,但这个脏题是真的脏,这还是不掉,只能拿到50分。
我们继续思考,为什么会超时,经过思考之后发现,由于程序分配伐木场数是枚举的,所以可能会出现以下情况: 该点之下的点只剩下n个,而可用的伐木场数k>n;
在这种情况下,我们不必继续搜索,直接返回0即可,加上这个优化,就终于能拿到这不容易的AC了

/*
jzoj	1306 
2017  3  27 ;
by century
*/
#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int dis[100000];
int w[100000];
int v[100000];
int a[1000][1000];
int top[10000];
int child[1000000];
int brother[1000000];
int n,k;
int f[200][200][200];
int ppp[10000];
int diss(int x,int dix,int father)
{
	dis[x]=dix;
	for(int i=1;i<=n;i++)
	{
		if(a[x][i]!=-1)
		{
			if(i!=father)
			ppp[x]+=diss(i,dix+a[x][i],x);
		}
	}
	return ppp[x]+1;
}
int vvv(int x)
{
	if(x==-1)return 0;
	ppp[x]=vvv(child[x])+vvv(brother[x]);
	return ppp[x]+1;
}
int dfs(int x,int str,int num,int k)
{
	if(x==-1)return 0;
	if(f[x][num][str])return f[x][num][str];
	if(num>ppp[x])return 0;
	int ans=-1;
	//x不选
	for(int i=0;i<=num;i++)
	{
		if(ans==-1||ans>dfs(child[x],str,i,k+1)+dfs(brother[x],str,num-i,k+1))
		ans=dfs(child[x],str,i,k+1)+dfs(brother[x],str,num-i,k+1);	
	}
	if(ans==-1)
	ans=w[x]*(dis[x]-dis[str]);
	else ans+=w[x]*(dis[x]-dis[str]);
	if(num>0&&x!=0)//x选
	{
		int sum=-1;
		int kk=num-1;
		for(int i=0;i<=kk;i++)
		{
			if(sum==-1||dfs(child[x],x,i,k+1)+dfs(brother[x],str,kk-i,k+1)<sum)
				sum=dfs(child[x],x,i,k+1)+dfs(brother[x],str,kk-i,k+1);
		}
		if(sum==-1)sum=0;
		return f[x][num][str]=min(ans,sum);
	}
	return f[x][num][str]=ans;
}
int main()
{
	memset(a,-1,sizeof(a));
	memset(child,-1,sizeof(child));
	memset(brother,-1,sizeof(brother));
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		int vv;
		cin>>w[i]>>vv>>v[i];
		a[i][vv]=v[i];
		a[vv][i]=v[i];
		brother[i]=child[vv];
		child[vv]=i;
	}
	diss(0,0,-1);
	vvv(0);
	cout<<dfs(0,0,k,0)<<endl;
	return 0;
}

  










原文地址:https://www.cnblogs.com/Lazers/p/6632728.html