皇宫看守 LOJ10157
皇宫看守(程序文件名:guard.pas)
问题描述:太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。
皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
编程任务:帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
数据输入:输入数据由文件名为guard.in的文本文件提供。输入文件中数据表示一棵树,描述如下:
第1行 n,表示树中结点的数目。
第2行至第n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号i(0<i<=n),在该宫殿安置侍卫所需的经费k,该边的儿子数m,接下来m个数,分别是这个节点的m个儿子的标号r1,r2,...,rm。
对于一个n(0 < n <= 1500)个结点的树,结点标号在1到n之间,且标号不重复。
数据输出:输出到guard.out文件中。输出文件仅包含一个数,为所求的最少的经费。
题解:
放置守卫使这棵树被看守 设
为w点放置守卫 子树全部被看守的最优解
为x别他的父亲看守可以x节点上无人的最优解,在这种状态下父节点必须放看守
为x的子节点放置看守 x点无人
状态转移方程
flag表示是否选取了 flag=0表示没去,flag=1表示取
时必须选一个子节点,我们可以维护一个最小的
当flag=0时加上
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=2200;
struct E{int x,y,next;}mm[MAXN<<1];
int w[MAXN],h[MAXN],len,f[MAXN][3],n;
inline void ins(int x,int y){
++len;
mm[len].x=x;mm[len].y=y;mm[len].next=h[x];h[x]=len;
}
void dfs(int x,int fa){
int sum1=0,sum2=0,flag=0,fake=0x7fffffff;
for(int k=h[x];k;k=mm[k].next){
int y=mm[k].y;
if(y==fa) continue;
dfs(y,x);
sum1+=min(f[y][0] , min(f[y][1],f[y][2]));
if(f[y][0]<=f[y][2]){
sum2+=f[y][0];
flag=1;
}else {
sum2+=f[y][2];
fake=min(fake,f[y][0]-f[y][2]);
}
}
f[x][0]=sum1+w[x];
f[x][1]=sum2;
f[x][2]=sum2;
if(!flag) f[x][2]+=fake;
}
int main(){
//freopen("guard.in","r",stdin);
scanf("%d",&n);
memset(h,0,sizeof h);len=0;//memset(f,127,sizeof f);
for(int i=1,x,t,y;i<=n;i++){
scanf("%d",&x);scanf("%d%d",&w[x],&t);
for(int j=1;j<=t;j++){
scanf("%d",&y);
ins(x,y);ins(y,x);
}
if(t==0){
f[x][1]=0;f[x][0]=f[x][2]=w[x];
}
}
dfs(1,0);
printf("%d\n",min(f[1][0],f[1][2]));
return 0;
}