• P6082 [JSOI2015]salesman


    题面

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

    Input

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

    Output

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

    思路

    • 树形dp的入门题.(确定是省选的题?)

    • 注意到次数限制(t)其实就是到达该点后,最多再进入它的(t−1)颗子树.

    • (f[i])表示从(i)节点向下方走,最后回到(i)的最大收益.

    • (g[i])表示取得(f[i])这个最大收益的方案是否唯一.

    • 考虑状态转移,若记每个点的收益为(w),停留次数为(t).

    • (f[i])就为(w[i])加上最多(t−1)个子树的收益.将儿子按照(f)排序即可.

    • (g[i])

      在满足以下3中情况中任意一种时为

      • 某个取得的儿子(f)值为(0).(我们可以选择不取它).
      • 某个取得的儿子(g)值为(0).(我们在这颗子树中有不同的路径)
      • 下个未选的儿子(如果有)和最后选择的儿子(f)值相同.(可以替换).
    • 其他时候(g[i])均为(1).

    • 答案即为(f[1],g[1]).

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LoveLive;
    const int inf = 0x7fffffff;
    inline int read()
    {
    	int x=0;
    	int f=1;
    	char ch;
    	ch=getchar();
    	while(ch>'9'||ch<'0')
    	{
    		if(ch=='-') f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		x=x*10,x=x+ch-'0';
    		ch=getchar();
    	}
    	return x*f;
    }
    const int MAXN = 2e5 + 10;
    int t[MAXN];
    int head[MAXN], idx;
    int ver[MAXN << 1], ne[MAXN << 1];
    inline void add(int u, int v) {
      ver[idx] = v;
      ne[idx] = head[u];
      head[u] = idx;
      idx++;
    }
    int n;
    int w[MAXN];
    int f[MAXN], g[MAXN];
    bool cmp(int a, int b) { return f[a] > f[b]; }
    void dfs(int u, int father) {
      f[u] = w[u];
      g[u] = 1;
      vector<int> son;
      for (int i = head[u]; i != -1; i = ne[i]) {
        int j = ver[i];
        if (j == father) continue;
        dfs(j, u);
        son.push_back(j);
      }
      sort(son.begin(), son.end(), cmp);
      int siz = son.size();
      int limit = min(siz, t[u]);
      for (int i = 0; i < limit; i++) {
        if (f[son[i]] < 0) continue;
        f[u] += f[son[i]];
        if (f[son[i]] == 0) g[u] = 0;
        if (g[son[i]] == 0) g[u] = 0;
      }
    }
    int main() {
      memset(head, -1, sizeof(head));
      n = read();
      for (int i = 2; i <= n; ++i) w[i] = read();
      for (int i = 2; i <= n; ++i)
        t[i] = read(), --t[i];  // t[i]表示i最多能选取多少个子树
      for (int i = 1; i < n; ++i) {
        int u = read(), v = read();
        add(u, v);
        add(v, u);
      }
      w[1] = 0;
      t[1] = inf;
      dfs(1, 0);
      printf("%d
    %s
    ", f[1],
             g[1] ? "solution is unique" : "solution is not unique");
      return 0;
    }
    
  • 相关阅读:
    Python if __name__ == "__main__" 的含义
    自己用
    phpstorm && pycharm
    API Design for C++ 一本书值得一看
    std::set 使用
    Using Windows Web Services
    SOA 好好了解下
    NI Measurement Studio Enterprise 8.6
    那天看看
    内存映射 那天自己改改
  • 原文地址:https://www.cnblogs.com/bangdexuanyuan/p/14080739.html
Copyright © 2020-2023  润新知