/*
【题目描述】
一个重要的基地被分成了n个连通的区域,出于某种原因,这个基地以某一个区域为核心,呈一树形分布。
在每个区域里安排警卫的费用是不同的,而每个区域的警卫都可以望见其相邻的区域。如果一个区域有警卫或是被相邻区域的警卫望见,那它就是安全的,你的任务是:在确保所有的区域安全的状态下,使总费用最小。
【输入格式】
第一行n,表示树中结点的数目。
接下来n行,每行依次是:区域的编号;在此安排警卫的费用;它的子结点的个数m,然后往后m个数,为它的子结点编号。
【输出格式】
一行一个数,为最小费用。
【输入样例】
6
1 30 3 2 3 4
2 16 2 5 6
3 5 0
4 4 0
5 11 0
6 5 0
【输出样例】
25
对于每个点i,都有3种状态分别为:
要么在父亲结点安排警卫,即被父亲看到
要么在儿子结点安排警卫,即被儿子看到
要么安排警卫
对于i
i被父亲看到,这时i没有安排警卫,i的儿子要么安排警卫,要么被它的后代看到。
i被儿子看到,即i的某个儿子安排了警卫,其他儿子需要安排警卫或者被它的后代看到。
i安排了警卫,i的儿子可能还需要安排警卫,这样可能有更便易的方式照顾到它的后代;
所以i的儿子结点被i看到,可能安排警卫,可能被它的后代看到。
设f(i,0)表示i结点被父亲看到;
f(i,1)表示i被它的儿子看到;
f(i,2)表示在i安排警卫;
则状态转移方程为:
f[i][0]=∑min(f[x][2],f[x][1]);
f[i][1]=∑min(f[x][2],f[x][1])+cost[i],但最后还要确保其中要有至少一个f[x][2];
f[i][2]=∑min(f[x][0],f[x][1],f[x][2]);
剩下的就跟普通的树形DP一样了。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define MAXSIZE 105000
#define sf scanf
#define pf printf
#define __int64 long long
#define INF 0xfffffff
using namespace std;
int dp[MAXSIZE][3],cost[MAXSIZE],vis[MAXSIZE];
vector<int> tree[MAXSIZE];
void DFS(int r)
{
for(int i=0;i<tree[r].size();i++)
{
DFS(tree[r][i]);
}
dp[r][2]+=cost[r];
for(int i=0;i<tree[r].size();i++)
{
dp[r][0]+=min(dp[tree[r][i]][1],dp[tree[r][i]][2]);
dp[r][1]+=min(dp[tree[r][i]][1],dp[tree[r][i]][2]);
dp[r][2]+=min(min(dp[tree[r][i]][0],dp[tree[r][i]][1]),dp[tree[r][i]][2]);
}
//cout<<dp[r][0]<<" "<<dp[r][1]<<" "<<dp[r][2]<<endl;
int temp=0xfffffff;
for(int i=0;i<tree[r].size();i++)
{
temp=min(temp,dp[tree[r][i]][2]+dp[r][1]-min(dp[tree[r][i]][1],dp[tree[r][i]][2]));
}
dp[r][1]=temp;
return ;
}
int main()
{
freopen("in.txt","r",stdin);
int u,v,n,m,C;
while(~sf("%d",&n))
{
for(int i=0;i<=n;i++)
{
tree[i].clear();
vis[i]=0;
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
sf("%d%d%d",&u,&C,&m);
cost[u]=C;
for(int j=1;j<=m;j++)
{
sf("%d",&v);
tree[u].push_back(v);
vis[v]=1;
}
}
int ans;
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
DFS(i);
//cout<<i<<endl;
ans=min(dp[i][1],dp[i][2]);
break;
}
}
cout<<ans<<endl;
}
}
NOI 警位安排(树形DP)
最新推荐文章于 2022-08-14 20:51:52 发布