NKOI 1472 警卫安排

来源:互联网 发布:花生壳域名怎么用 编辑:程序博客网 时间:2024/04/30 06:36

警卫安排

Time Limit:10000MS  Memory Limit:65536K
Total Submit:34 Accepted:18 
Case Time Limit:1000MS

Description

一个重要的基地被分为n个连通的区域。出于某种神秘的原因,这些区域以一个区域为核心,呈一颗树形分布。 
在每个区域安排警卫所需要的费用是不同的,而每个区域的警卫都可以望见其相邻的区域,只要一个区域被一个警卫望见或者是安排有警卫,这个区域就是安全的。你的任务是:在确保所有区域都是安全的情况下,找到安排警卫的最小费用。

Input

第一行n,表示树中结点的数目。 
接下来的n行描述了n个区域的信息,每一行包含的整数依次为:区域的标号i(0<i<=n),在区域i安排警卫的费用k,区域i的子结点数目m,接下来m个数为区域i的子结点编号。

Output

一行一个整数,为最小的安排费用。

Sample Input

61 30 3 2 3 42 16 2 5 63 5 04 4 05 11 06 5 0

Sample Output

25

Hint

对于所有的数据,0<n<=720。 



我们可以很显然的发现这是一道树形DP

我们讨论结点i:
     i不安排警卫,i被父亲望到,这时i没有安排警卫,i的儿子要么安排警卫,要么被它的后代望到。
     i不安排警卫,i被儿子望到,即i的某个儿子安排了警卫,其他儿子需要安排警卫或者被它的后代望到。
     i安排了警卫,i的儿子结点被i望到,可能安排警卫,可能被它的后代看到。

状态:f[i][j]表示以i为根这棵子树按排警卫所需最小费用(其中i号节点采用的是第j种监视方式)

j代表监视方式,分为0,1,2三种

f[i][0]表示在第i号节点安排一个警卫,自己望,的最小费用 
f[i][1]表示由i的儿子监视第i号位置,被儿子望,的最小费用 
f[i][2]表示由i的父亲监视第i号位置 ,被父亲望,的最小费用

f[i][0]=  min{ f[son[1]][0] , f[son[1]][1] , f[son[1]][2] }
            +min{ f[son[2]][0] , f[son[2]][1] , f[son[2]][2] }
            +......
            +min{ f[son[k]][0] , f[son[k]][1] , f[son[k]][2] } + cost[i]
             (i的儿子数为k)

f[i][2]=  min{ f[son[1]][0] , f[son[1]][1] }+min{ f[son[2]][0] , f[son[2]][1]}
            +......+min{ f[son[k]][0] , f[son[k]][1] } 

至于f[I][1],我们思考:

f[i][1]是在i节点,有儿子望到i的情况,而f[i][2]是在i节点,他的父节点望到他的情况

对于f[i][1],i的所有儿子中一定有一个儿子自己安排了警卫,而对于f[i][2],i的儿子除了可以靠自己安排,也可以让自己的后代安排

因此,f[i][1]与f[i][2]满足关系f[i][1]=min{f[i][2]-min(f[s][0],f[s][1])+f[s][0]}

其中s为i的儿子

最后输出答案的时候,由于根节点没有父节点,所以输出min(f[root][0],f[root][1])

#include<cstdio>#include<iostream>using namespace std;const int maxn=1500,inf=1e9;int NEXT[maxn],END[maxn],LAST[maxn],n;int cost[maxn],cnt,f[maxn][4];bool mark[maxn];void insert(int a,int b){      END[++cnt]=b;      NEXT[cnt]=LAST[a];      LAST[a]=cnt;  }void DP(int p){int i,j;if(LAST[p]==0)f[p][0]=cost[p];f[p][1]=inf;return;}for(i=LAST[p],j=END[i];i;i=NEXT[i],j=END[i])DP(j);f[p][0]=cost[p];f[p][1]=inf;for(i=LAST[p],j=END[i];i;i=NEXT[i],j=END[i]){f[p][0]+=min(f[j][0],min(f[j][1],f[j][2]));f[p][2]+=min(f[j][0],f[j][1]);}for(i=LAST[p],j=END[i];i;i=NEXT[i],j=END[i])    f[p][1]=min(f[p][1],f[p][2]-min(f[j][0],f[j][1])+f[j][0]);}int main(){int i,j,x,y,m,k;scanf("%d",&n);for(i=1;i<=n;i++){scanf("%d%d%d",&x,&k,&m);cost[x]=k;for(j=1;j<=m;j++){scanf("%d",&y);insert(x,y);mark[y]=1;//mark记录当前节点是不是一个子节点}}for(i=1;i<=n;i++)//找根节点    if(!mark[i]){k=i;break;}DP(k);cout<<min(f[k][0],f[k][1]);}

1 0
原创粉丝点击