zoukankan      html  css  js  c++  java
  • BZOJ 4472 salesman 题解

    题目

    某售货员小T要到若干城镇去推销商品,由于该地区是交通不便的山区,任意两个城镇之间都只有唯一的可能经过其它城镇的路线。小T可以准确地估计出在每个城镇停留的净收益。这些净收益可能是负数,即推销商品的利润抵不上花费。由于交通不便,小T经过每个城镇都需要停留,在每个城镇的停留次数与在该地的净收益无关,因为很多费用不是计次收取的,而每个城镇对小T的商品需求也是相对固定的,停留一次后就饱和了。每个城镇为了强化治安,对外地人的最多停留次数有严格的规定。请你帮小T设计一个收益最大的巡回方案,即从家乡出发,在经过的每个城镇停留,最后回到家乡的旅行方案。你的程序只需输出最大收益,以及最优方案是否唯一。方案并不包括路线的细节,方案相同的标准是选择经过并停留的城镇是否相同。因为取消巡回也是一种方案,因此最大收益不会是负数。小T在家乡净收益是零,因为在家乡是本地人,家乡对小T当然没有停留次数的限制。

    输入格式

    输入的第一行是一个正整数(n(5<=n<=100000)),表示城镇数目。城镇以(1)(n)的数命名。小T的家乡命名为(1)。第二行和第三行都包含以空格隔开的(n-1)个整数,第二行的第(i)个数表示在城镇(i+1)停留的净收益。第三行的第(i)个数表示城镇(i+1)规定的最大停留次数。所有的最大停留次数都不小于(2)。接下来的(n-1)行每行两个1到(n)的正整数(x)(y),之间以一个空格隔开,表示(x)(y)之间有一条不经过其它城镇的双向道路。输入数据保证所有城镇是连通的。

    输出格式

    输出有两行,第一行包含一个自然数,表示巡回旅行的最大收益。如果该方案唯一,在第二行输出"solution is unique",否则在第二行输出"solution is not unique"。

    输入样例

    9
    -3 -4 2 4 -2 3 4 6
    4 4 2 2 2 2 2 2 
    1 2
    1 3
    1 4
    2 5
    2 6
    3 7
    4 8 
    4 9
    

    输出样例

    9
    solution is unique
    

    样例解释

    最佳路线包括城镇 1,2, 4, 5, 9

    题解

    任意两个城镇之间都只有唯一的可能经过其它城镇的路线,说明这一定是一棵树

    第一问

    这道题明显是树形动规,但加了一个限制条件,就是最大停留次数,联想树形动规时的形式,这个最大停留次数其实和能访问的子树个数有关系.

    上图表示了一个DFS过程,其中2,3,4,5,6号都是子树,若将这些子树看成一个点,则DFS过程中经过的节点为

    [1] 2 [1] 2 [1] 4 [1] 5 [1] 6 [1]
    

    注意其中的根节点1,出现了6次,本题中就是停留了6次,而1号节点有5棵子树,可以发现,若i节点最大停留次数为(limit[x]),则DFS中最多能访问(limit[x]-1)棵子树

    这些子树中对根节点dp的贡献不同,我们当然要选择其中最大的,所以排一下序,选其中前(limit[x]-1)棵子树来更新根节点的dp值.

    注意,(limit[x]-1)也存在大于子树个数的情况,所以实际操作的时候要取(limit[x])和子树个数的最小值作为更新根节点的子树数量,由于净收益可能是负数,所以更新的时候发现是负数立刻停止即可.

    所以遍历子树(已排序)时候,条件为

    soni < min(limit[root] - 1, sontot) && dp[sonn[soni + 1]] >= 0
    

    其中,soni为当前循环遍历的子树时的循环变量(注意不时子树根节点编号),root为根节点编号,sontot为子树的数量,sonn数组保存子树的根节点编号,sonn[soni+1]为这次循环的子树编号(因为soni在循环内自增1)

    还有一个条件

    家乡对小T当然没有停留次数的限制

    也就是整棵树的根节点无限制次数,那么只需要在DFS之前将根节点的limit值赋值为无限大即可

    第二问

    显然,根节点方案是否唯一首先要看其子树,如果有任意一棵更新了根节点dp值得子树的方案不唯一,根节点的方案显然也不唯一.

    除此之外,还有存在子树方案唯一但根节点选取子树的方案不唯一的情况.

    遍历子树的时候,将 子树方案是否唯一的值 和 根节点选取子树的方案是否唯一 的值进行或运算(只要有一个为真,结果就为真),得到的结果就是这棵树的方案是否唯一,最后输出即可.

    那么怎么判断根节点选取子树的方案是否唯一呢,有两种情况:

    1. 相同值引起的不唯一

    假设排好序后的子树dp值为10 9 8 7 6 6 5 4 3 2 1,而你只能选5个(limit值为6),显然你选择的是10 9 8 7 6,但是仔细观察,你会发现还有一个相同的6,那么我能不能抛弃第一个6选择第二个6呢?当然可以,那么,这就有了两种选择办法([10] [9] [8] [7] [6] 6 5 4 3 2 1[10] [9] [8] [7] 6 [6] 5 4 3 2 1),这种情况不需要注意limit值的限制,因为没有多选择一个,是抛弃一个再选一个,用来更新的子树数量没有改变.

    1. dp值为0引起的不唯一

    假设排好序后的子树dp值为10 9 8 7 6 0,而你只能选5个(limit值为6),你可以选择10 9 8 7 6,也可以选择10 9 8 7 6 0,这两种方案更新的值都是一样的,这种情况需要注意limit值的限制,即使limit值为0,如果选择了,也会多一次停留,虽然数据有点水,亲测加不加limit值的限制都能A.

    出现这两种情况时,直接将这棵树方案是否唯一的赋值为真即可

    代码

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int N = 100000;
    struct edge {
        int i, next;
    } edges[2 * N + 5];
    int head[N + 5], tot, n, w[N + 5], limit[N + 5], dp[N + 5], ansn[N + 5],sonn[N + 5];
    void add(int u, int v) {
        edges[++tot].i = v;
        edges[tot].next = head[u];
        head[u] = tot;
    }
    bool cmp(int a, int b) { return dp[a] > dp[b]; }
    void dfs(int root, int f) {
        dp[root] = w[root];
        int sontot = 0, soni = 0;
        for (int i = head[root]; i; i = edges[i].next)
            if (edges[i].i != f) dfs(edges[i].i, root);
        for (int i = head[root]; i; i = edges[i].next)
            if (edges[i].i != f) sonn[++sontot] = edges[i].i;
        sort(sonn + 1, sonn + 1 + sontot, cmp);
        while (soni < min(limit[root] - 1, sontot) && dp[sonn[soni + 1]] >= 0)
            dp[root] += dp[sonn[++soni]], ansn[root] |= ansn[sonn[soni]];//按位或
        if (soni < sontot && soni > 0 && dp[sonn[soni]] == dp[sonn[soni + 1]] || dp[sonn[soni]] == 0 && soni > 0 && soni <= limit[root] - 1)//两种情况,注意边界
            ansn[root] = 1;
    }
    int main() {
        scanf("%d", &n);
        for (int i = 1; i < n; i++) scanf("%d", &w[i + 1]);
        for (int i = 1; i < n; i++) scanf("%d", &limit[i + 1]);
        for (int i = 1; i < n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            add(u, v);
            add(v, u);
        }
        limit[1] = n + 1;//在家乡没有停留限制
        dfs(1, 0);
        printf("%d
    %s", dp[1], ansn[1] ? "solution is not unique" : "solution is unique");
        return 0;
    }
    
  • 相关阅读:
    在 Mac 上使用 PICT 进行 Pairwise 测试
    组合测试术语:Pairwise/All-Pairs、OATS(Orthogonal Array Testing Strategy)
    小白懂算法之二分查找
    小白也能看懂的JVM内存区域
    小白也能看懂的JDK1.8前_HashMap的扩容机制原理
    小白也能看懂的ArrayList的扩容机制
    activenq整合spring之队列消费者
    activemq整合spring之队列生产者
    ActiveMQ之Broker
    ActiveMQ_JMS签收
  • 原文地址:https://www.cnblogs.com/youxam/p/bzoj4472-salesman.html
Copyright © 2011-2022 走看看