题目地址
五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序。
已知整个地下超市的所有通道呈一棵树的形状;某些通道之间可以互相望见。总经理要求所有通道的每个端点(树的顶点)都要有人全天候看守,在不同的通道端点安排保安所需的费用不同。
一个保安一旦站在某个通道的其中一个端点,那么他除了能看守住他所站的那个端点,也能看到这个通道的另一个端点,所以一个保安可能同时能看守住多个端点(树的结点),因此没有必要在每个通道的端点都安排保安。
编程任务:
请你帮助超市经理策划安排,在能看守全部通道端点的前提下,使得花费的经费最少。
第1行 n,表示树中结点的数目。
第2行至第n+1行,每行描述每个通道端点的信息,依次为:该结点标号i(0
对于一个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
样例说明:在结点2,3,4安置3个保安能看守所有的6个结点,需要的经费最小:25
表示以x为根的树所有结点被覆盖的前提下:
f[x][0]:当前的 x 结点由自身安放的路由覆盖
f[x][1]:当前的 x 结点由子节点的路由覆盖
f[x][2]:当前的 x 结点由父节点的路由覆盖
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N = 1510,inf = 1e9+7;
int n,cnt,h[N],val[N],ne[N<<1],e[N<<1],idx;
int f[N][4];
void add(int a,int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
inline void dp(int x,int fa)
{
f[x][0] = val[x];
int sum = 0,mincost = inf;//sum记录有多少个子结点安放路由
// 遍历当前结点的所有子结点
for(int i = h[x]; ~i; i = ne[i])
{
int y = e[i];
if(y == fa) continue;
dp(y,x);
// 当前结点安放路由覆盖自己,子结点随意
f[x][0] += min(min(f[y][0],f[y][1]),f[y][2]);
// 当前结点被子结点覆盖,即当前结点不安放路由,不存在子结点被父结点覆盖的情况
f[x][1] += min(f[y][0],f[y][1]);
// 特判:所有字结点都不安放路由的情况
if(f[y][0] < f[y][1])sum++;
else
mincost = min(mincost,f[y][0]- f[y][1]);
// 当前结点由父结点覆盖
f[x][2] += min(f[y][0],f[y][1]);//子结点就不能为父结点所覆盖了
}
if(!sum)
f[x][1] += mincost;
}
int main()
{
cin >> n;
int x;
memset(h,-1,sizeof(h));
for(int i = 1; i <= n; i++)
{
cin >> x;
cin >> val[x];
int num;
cin >> num;
while(num--)
{
int y;
cin >> y;
add(x,y);
add(y,x);
}
}
dp(1,0);
printf("%d",min(f[1][0],f[1][1]));
return 0;
}
大佬题解