题目链接:http://acm.ocrosoft.com/problem.php?cid=1630&pid=16
题目描述:
太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。
皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;有边直接相连的宫殿可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
编程任务:帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
输入文件中数据表示一棵树,描述如下:
第1行 n,表示树中结点的数目。
第2行至第n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号i(0),在该宫殿安置侍卫所需的经费K,该点的儿子数M,接下来M个数,分别是这个节点的M个儿子的标号R1,R2,...,RM。
对于一个n(0 < n<=1500)个结点的树,结点标号在1到n之间,且标号不重复。
输出文件仅包含一个数,为所求的最少的经费。
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
思路:
一个树形dp问题,分别用dp[i][0]表示i点放看守,dp[i][1]表示i点不放看守i点被儿子监视,dp[i][2]表示i点不放看守i点被父亲节点监视。
(1) dp[i][0] = 所有子节点t的dp[t][0], dp[t][1], dp[t][2]中最小的一个的和 + vi[i] (min(dp[t][0], min(dp[t][1], dp[t][2]))+vi[i])
(2) dp[i][1] = 某个子节点放看守 + 其他节点的dp[t][0], dp[t][1]中最小的一个的和
(3) dp[i][2] = 所有子节点的dp[t][1]的和
代码:
#include
using namespace std;
#define ll long long
#define maxn 1505
#define inf 0x3f3f3f3f
int n, v[maxn], son_num[maxn],son[maxn][maxn],vis[maxn];
ll dp[maxn][3], tmp[maxn];//dp[i][0] i点放看守,dp[i][1] i点不放看守i点被儿子监视,dp[i][2] i点不放看守i点被父节点监视
void tree(int x)
{
if (dp[x][0])return;
for (int i = 1; i <= son_num[x]; i++)
{
int t = son[x][i];//记录子节点的序号
tree(t);//树形dp
dp[x][0] += min(dp[t][0], min(dp[t][1], dp[t][2]));//如果再x位置上放了守卫,那随便加子节点的最小值就行
dp[x][2] += dp[t][1]; //如果x位置是被父节点保护的,且x不放首位,为了x的子节点被保护,那x只能有x的子节点的子节点保护
}
dp[x][0] += v[x];//加上自己位置放上守卫的价格
memset(tmp, 0, sizeof(tmp));
ll tt = 0;//如果x位置是由子节点保护的
for (int i = 1; i <= son_num[x]; i++)
{
int t = son[x][i];
tmp[i] = min(dp[t][0], dp[t][1]);//并不需要每个子节点都有守卫
tt += tmp[i];
}
dp[x][1] = inf;//初始化无穷大
for (int i = 1; i <= son_num[x]; i++)
{
int t = son[x][i];
if (tt - tmp[i] + dp[t][0] < dp[x][1])//保证x位置的子节点至少有一个守卫
dp[x][1] = tt - tmp[i] + dp[t][0];
}
}
int main()
{
int root;//根
cin >> n;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
cin >> v[x] >> son_num[x];//录入每个节点放守卫的价值和每个节点有几个子节点
for (int j = 1; j <= son_num[x]; j++)
{
cin >> son[x][j];
vis[son[x][j]] = 1;//所有的子节点只要存在记录为1
}
}
for (int i = 1; i <= n; i++)
{
if (!vis[i])//不为1的第一个节点作为根
{
root = i;
break;
}
}
tree(root);//树形dp
cout << min(dp[root][0],dp[root][1]) << endl;
}