zoukankan      html  css  js  c++  java
  • P1273 有线电视网

      一开始我写的普通的树形dp,但是只有10分……(泪流成河)

      讲一讲后来想到的正解:

      由于问最多选多少,又要满足最优解下传,那么我们自然规定 f [ i ] [ j ] 为以 i 为根的子树中,选取 j 个点所能获得的最大利润,当然这些点不包括中转站。

      那么最后我们从 n 往前看一遍,如果存在 f [ 1 ] [ i ] 大于等于0,那么就满足不亏本,直接输出就可以了

      那,在一个节点,我们就不仅仅是看它的子节点而应该看它的子节点所在的子树,也就是说我们会看 f [ v ] [ i ~ size [ v ] ] ,来更新 u 。那么就要用到分组背包,在枚举边的时候就是在枚举组数,枚举 u 的节点个数就是在枚举背包容量,在枚举子树的取点情况就是在枚举每一组的具体情况。

      对于一开始我写的树形dp为什么不对,存在一个致命的错误!我之前的思路是每一次如果当前子树会至少不亏本,就ans下传,并且更新当前节点。但是事实上不需要每一次转移都不亏本,这次的亏本可以靠下一次的盈利赚回来,所以我们必须记录所有状态,最后找最优的!那么只能用树上分组dp,单单普通树dp是不行的。

      下面是代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define maxn 3000
    struct node
    {
        int to,nxt,cost;
    } edge[10000010];
    int f[maxn][maxn],val[maxn],head[maxn];
    int cnt,n,m;
    void add(int a,int b,int c)
    {
        edge[++cnt].to=b;
        edge[cnt].nxt=head[a];
        head[a]=cnt;
        edge[cnt].cost=c;
    }
    int dp(int u,int fa)
    {
        if (u>=n-m+1)
        {
            f[u][1]=val[u];
            return 1;
        }
        int tot=0;
        for(int i=head[u]; i; i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(v==fa) continue;
            int son=dp(v,u);
            tot+=son;
            for(int j=tot; j>=0; j--)
                for(int k=0; k<=son; k++)
                    if(k<=j)
                        f[u][j]=max(f[u][j-k]+f[v][k]-edge[i].cost,f[u][j]);
        }
        return tot;
    }
    int main()
    {
        memset(f,~0x3f,sizeof(f));
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n-m; i++)
        {
            int k;
            scanf("%d",&k);
            for(int j=1; j<=k; j++)
            {
                int x,c;
                scanf("%d%d",&x,&c);
                add(i,x,c);
                add(x,i,c);
            }
        }
        for(int i=n-m+1; i<=n; i++)
            scanf("%d",&val[i]);
        for(int i=1; i<=n; i++)
            f[i][0]=0;
        dp(1,0);
        for(int i=n; i>=0; i--)
            if(f[1][i]>=0)
            {
                printf("%d",i);
                return 0;
            }
        printf("0");
        return 0;
    }
  • 相关阅读:
    dbgrideh标题排序
    ctrl r w 去掉
    c# 中@ 的三种用法
    vs插件
    oracle查看会话(常规操作)
    3 docker容器
    k8s-组件
    k8s-常见错误
    k8s监控-kube-prometheus
    helm-私有仓库
  • 原文地址:https://www.cnblogs.com/popo-black-cat/p/10341148.html
Copyright © 2011-2022 走看看