【ssl1608】皇宫看守【树形DP】

Description

太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。
皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。

Input

帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。

Output

输入文件中数据表示一棵树,描述如下:
第1行 n,表示树中结点的数目。
第2行至第n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号i(0对于一个n(0 < n<=1500)个结点的树,结点标号在1到n之间,且标号不重复。

Sample Input

6
1 30 3 2 3 4
2 16 2 5 6
3 5 0
4 4 0
5 11 0
6 5 0

输出文件仅包含一个数,为所求的最少的经费。

Sample Output

25

分析

f [ i ] [ 0 ] f[i][0] f[i][0] 表示当前节点站士兵;
f [ i ] [ 2 ] f[i][2] f[i][2] 表示当前节点不站士兵,被父节点监视。
f [ i ] [ 1 ] f[i][1] f[i][1]表示当前节点不站士兵,被他其中一个子节点监视。

有三种情况:
  1. 第dep个节点有士兵了,那么子节点放与不放都可以。
f[dep][0]+=min(f[a[i].to][0],min(f[a[i].to][1],f[a[i].to][2]));
	}
	f[dep][0]+=w[dep];//因为放了,所以最后要加上他的经费(在循环外)
  1. 第dep个节点被父节点监视,所以第dep节点的子节点不会被监视到,那么子节点的子节点 就要放士兵,用来监视第dep节点的子节点。
    f [ d e p ] [ 2 ] + = f [ a [ i ] . x ] [ 1 ] ; f[dep][2]+=f[a[i].x][1]; f[dep][2]+=f[a[i].x][1];
  2. 因为被子节点监视,只需要一个子节点监视即可,其他子节点可放可不放,所以先对子节点可放可不放的和做一个预处理。
	for(int i=h[dep];i;i=a[i].next)
	{
		tmp[i]=min(f[a[i].to][1],f[a[i].to][0]);
		tt+=tmp[i];
	}

min(所有子节点可放可不放的和- 当前子节点可放可不放+当前子结点必须放(f[a[i].to][0]))
这个式子就可以求出当前子节点放士兵监视的值了

	for(int i=h[dep];i;i=a[i].next)
		f[dep][1]=min(tt-tmp[i]+f[a[i].x][0],f[dep][1]);

上代码

#include
#include
#include
#include
using namespace std;
typedef long long ll;//不开longlong见祖宗 
ll n,h[10001],v[10001],f[10001][5],s[10001],ans,tot;
struct node
{
	ll to,next;
}a[100001];

void add(ll x,ll y)
{
	a[++tot]=(node){y,h[x]};
	h[x]=tot;
}

void dp(ll k)
{
	for(ll i=h[k];i>0;i=a[i].next)
	{
		ll y=a[i].to;
		dp(y);
		f[k][1]+=min(f[y][1],min(f[y][2],f[y][3]));
		f[k][3]+=f[y][2];
	}	
	f[k][1]+=s[k];
	ll t=0;
	for(ll i=h[k];i>0;i=a[i].next)
	{
		ll y=a[i].to;
		t+=min(f[y][1],f[y][2]);
	}
	f[k][2]=0x7fffffff;
	for(ll i=h[k];i>0;i=a[i].next)
	{
		ll y=a[i].to;
		f[k][2]=min(f[k][2],t-min(f[y][1],f[y][2])+f[y][1]);
	}
}

int main()
{
    cin>>n;
    for(ll i=1;i<=n;i++)
    {
    	ll x,k;
    	cin>>x;
    	cin>>s[x]>>k;
    	for(ll j=1;j<=k;j++)
    	{
    		ll y;
    		cin>>y;
    		add(x,y);
    		v[y]=1;//用于判断根节点 
    	}
    } 
    for(ll i=1;i<=n;i++)
    {
    	if(v[i]==0)
    	{
    		dp(i);
    		cout<<min(f[i][1],f[i][2]);
    	}
    }
	return 0;
}

你可能感兴趣的:(DP,DFS,SSL题库)