【ACWing】323. 战略游戏

题目地址:

https://www.acwing.com/problem/content/325/

鲍勃喜欢玩电脑游戏,特别是战略游戏,但有时他找不到解决问题的方法,这让他很伤心。现在他有以下问题。他必须保护一座中世纪城市,这条城市的道路构成了一棵树。每个节点上的士兵可以观察到所有和这个点相连的边。他必须在节点上放置最少数量的士兵,以便他们可以观察到所有的边。你能帮助他吗?

输入格式:
输入包含多组测试数据,每组测试数据用以描述一棵树。对于每组测试数据,第一行包含整数 N N N,表示树的节点数目。接下来 N N N行,每行按如下方法描述一个节点。
节点编号:(子节点数目) 子节点 子节点 …
节点编号从 0 0 0 N − 1 N−1 N1,每个节点的子节点数量均不超过 10 10 10,每个边在输入数据中只出现一次。

输出格式:
对于每组测试数据,输出一个占据一行的结果,表示最少需要的士兵数。

数据范围:
0 < N ≤ 1500 0<N≤1500 0<N1500

思路是动态规划。设 f [ i ] [ 0 , 1 ] f[i][0,1] f[i][0,1]分别是以 i i i为根的子树中,不取 i i i和取 i i i两种情况下,分别需要的最少士兵数。设 i i i的孩子分别是 x 1 , . . . , x n x_1,...,x_n x1,...,xn,当求 f [ i ] [ 0 ] f[i][0] f[i][0]的时候,由于 i i i是不取的,那么其所有孩子都必须取,以保证 ( i , x 1 , . . . , n ) (i,x_{1,...,n}) (i,x1,...,n)这些边是覆盖的,所以有: f [ i ] [ 0 ] = ∑ 1 ≤ k ≤ n f [ x k ] [ 1 ] f[i][0]=\sum_{1\le k\le n} f[x_k][1] f[i][0]=1knf[xk][1]当求 f [ i ] [ 0 ] f[i][0] f[i][0]的时候,由于 i i i是取的,所以其每个孩子可取可不取,所以有: f [ i ] [ 1 ] = 1 + ∑ 1 ≤ k ≤ n max ⁡ { f [ x k ] [ 0 ] , f [ x k ] [ 1 ] } f[i][1]=1+\sum _{1\le k\le n}\max\{f[x_k][0],f[x_k][1]\} f[i][1]=1+1knmax{ f[xk][0],f[xk][1]}代码如下:

#include <iostream>
#include <cstring>
using namespace std;

const int N = 1510;

int n;
int h[N], e[N], ne[N], idx;
int f[N][2];
bool is_root[N];

void add(int a, int b) {
    
    
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int u) {
    
    
    int c0 = 0, c1 = 1;
    for (int i = h[u]; ~i; i = ne[i]) {
    
    
        int j = e[i];
        dfs(j);

        c0 += f[j][1];
        c1 += min(f[j][0], f[j][1]);
    }

    f[u][0] = c0;
    f[u][1] = c1;
}

int main() {
    
    
    while (scanf("%d", &n) != -1) {
    
    
    	// 重置一下各个数组
        memset(h, -1, sizeof h);
        memset(is_root, true, sizeof is_root);
        memset(f, 0, sizeof f);
        idx = 0;

        for (int i = 0; i < n; i++) {
    
    
            int id, cnt;
            scanf("%d:(%d)", &id, &cnt);
            for (int j = 0; j < cnt; j++) {
    
    
                int a;
                cin >> a;
                add(id, a);
                is_root[a] = false;
            }
        }

        for (int i = 0; i < n; i++)
            if (is_root[i]) {
    
    
                dfs(i);
                cout << min(f[i][0], f[i][1]) << endl;
                break;
            }
    }

    return 0;
}

每组数据时空复杂度 O ( N ) O(N) O(N)

猜你喜欢

转载自blog.csdn.net/qq_46105170/article/details/114778045
323