zoukankan      html  css  js  c++  java
  • P2458 保安站岗/P2899 Cell Phone Network

    Link Link2

    desprition

    五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序。

    已知整个地下超市的所有通道呈一棵树的形状;某些通道之间可以互相望见。总经理要求所有通道的每个端点(树的顶点)都要有人全天候看守,在不同的通道端点安排保安所需的费用不同。

    一个保安一旦站在某个通道的其中一个端点,那么他除了能看守住他所站的那个端点,也能看到这个通道的另一个端点,所以一个保安可能同时能看守住多个端点(树的结点),因此没有必要在每个通道的端点都安排保安。

    编程任务:

    请你帮助超市经理策划安排,在能看守全部通道端点的前提下,使得花费的经费最少。

    solution

    一开始自己拿战略游戏那道题的思路写了这道题,然后就获得了 (10pts) 的好成绩。

    仔细想想这两道题之间还是有区别的:

    • 战略游戏是每条边的左右端点至少选一个。
    • 但这道题不太一样,这两个点可以都不选,比如左端点被他的父亲覆盖,然后右端点被他儿子覆盖。

    这样我们的状态设计就出现了问题。

    考虑到一个点只会被他自己,他儿子或者他父亲覆盖。

    然后就设 (f[i][0/1/2]) 分别表示 在这个点放/在这个点的儿子处放/在这个点的父亲处放的最小花费。

    初始化: (f[i][0] = a[i], f[i][1] = f[i][2] = 0)

    特别的当 (i) 为叶子节点时, (f[i][1] = inf) ,因为他下面没有儿子可以覆盖。

    转移

    1. (f[i][0] += min(f[to][0],f[to][1],f[to][2]))

    ​ 加入在 (i) 这个点放的话,那么他的儿子可以任意放。

    1. (f[i][2] += min(f[to][0],f[to][1]))

    ​ 这个时候因为 (i) 这个节点没有放,所以就不能由 (f[to][1]) 转移过来。

    1. (f[i][1] += min(f[to][0],f[to][1])) 至少选一个 (f[to][0])

    ​ 这个时候只需要满足至少有一个儿子节点放就行,其他儿子随便放。

    ​ 这个直接转移是 (O(n^2)) 的,考虑优化。

    ​ 记录一个差值 (tmp = min(tmp,max(0,f[to][0]-f[to][1])))

    ​ 如果 (tmp == 0) 说明我们至少选了 一个 (f[to][1]) ,而 (tmp) 不为零的时候直接把差值最小的那个即 (tmp) 加上去就行。

    最后答案 (min(f[1][0],f[1][1])) 因为 (1) 没有父亲节点,所以自然也不存在被父亲覆盖这种情况。

    又水了两道蓝题呢,(◕ᴗ◕✿)。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 1e5+10;
    const int inf = 2333333;
    int n,x,son,tot;
    int head[N],siz[N],a[N],f[N][3];
    struct node
    {
    	int to,net;
    }e[N<<1];
    inline int read()
    {
    	int s = 0,w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    void add(int x,int y)
    {
    	e[++tot].to = y;
    	e[tot].net = head[x];
    	head[x] = tot;
    }
    void dfs(int x,int fa)
    {
    	f[x][0] = a[x];
    	f[x][1] = f[x][2] = 0;
    	int tmp = inf;
    	for(int i = head[x]; i; i = e[i].net)
    	{
    		int to = e[i].to;
    		if(to == fa) continue;
    		dfs(to,x);
    		f[x][0] += min(f[to][0],min(f[to][1],f[to][2]));
    		f[x][1] += min(f[to][0],f[to][1]);
    		f[x][2] += min(f[to][0],f[to][1]);
    		tmp = min(tmp,max(0,f[to][0]-f[to][1]));
    	}
    	if(siz[x] == 0) f[x][1] = inf;
    	else f[x][1] += tmp;
    }
    int main()
    {
    	n = read();
    	for(int i = 1; i <= n; i++)
    	{
    		x = read(); a[x] = read(); siz[x] = read();
    		for(int i = 1; i <= siz[x]; i++)
    		{
    			son = read();
    			add(x,son); add(son,x);
    		}
    	}
    	dfs(1,1);
    	printf("%d
    ",min(f[1][0],f[1][1]));
    	return 0;
    }
    
  • 相关阅读:
    HDU 5640 King's Cake
    HDU 5615 Jam's math problem
    HDU 5610 Baby Ming and Weight lifting
    WHU1604 Play Apple 简单博弈
    HDU 1551 Cable master 二分
    CodeForces659C Tanya and Toys map
    Codeforces 960E 树dp
    gym 101485E 二分匹配
    Codeforces 961E 树状数组,思维
    Codeforces Round #473 (Div. 2) D 数学,贪心 F 线性基,模板
  • 原文地址:https://www.cnblogs.com/genshy/p/13866430.html
Copyright © 2011-2022 走看看