码迷,mamicode.com
首页 > 其他好文 > 详细

TELE poj1155 题解

时间:2018-11-25 22:24:19      阅读:231      评论:0      收藏:0      [点我收藏+]

标签:下标   int   class   pac   style   names   问题   for   i++   

很明显,这道题是以1为根的树,存在最优子问题,因此考虑树形DP。

先看一下样例

技术分享图片树本来有向,请见谅

常识:利润=收入-成本,也就是:叶节点点权-边权

那么更加明显用dp[i][j]来记录在以i为根节点,使j个用户可以收视的最大利润。空间:3200*3200*4/1024/1024=39.0625MB,明显开的下

现在,推方程:每一次更新dp[i][j],就相当于从i的该儿子中选取k个,再从除这个儿子以外的节点中选出j-k个,再减去将信号传给这个儿子的花费,就是从root到son边的权值

那么这样,很容易得出:为了计算dp[i][0],dp[i][1]...dp[i][size[i]],就枚举i的每一个儿子,然后,对于每一个儿子,枚举k,令dp[i][j]=max(dp[i][j-k]+dp[son][k]-pic[i][son])//为了方便,开了邻接矩阵

 1 for(int i=head[root];i!=-1;i=pic[i].nxt)//这里我用的链式前向星
 2 {
 3         int to=pic[i].to;
 4         for(int j=siz[root];j>=0;j--)
 5         {
 6             for(int k=min(siz[to],j);k>=0;k--)
 7             {
 8                 if(dp[root][j-k]!=-(1<<30))//dp初值设为负无穷,防止计算未算过的
 9                 {
10                     dp[root][j]=max(dp[root][j],dp[root][j-k]+dp[to][k]-pic[i].cot);
11                 }
12             }
13         }
14     }
15 }

现在,考虑枚举范围,如果每一次j,k都从n枚举到1,O(n3),这道题也许能卡过。

优化:显而易见,j大于root的儿子的总数的部分,k大于to儿子总数的部分,都是无意义的,于是,每一次都更新size[root],加上size[to],再进行枚举,这样,由于每一个点都只与其他的点组合一次,就成功的优化到了O(n2)

在进行dp和dfs后,如何计算答案呢?

很简单,扫一遍dp[1][m],dp[1][m-1]...dp[1][0]直到找到第一个非负值,输出下标即可

送上AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int p0,p1,p2;
struct edge
{
    int to,nxt,cot;
}pic[3200];
int head[3200];
int cnt=1;
int siz[3200];
int dp[3200][3200];
void add(int f,int t,int c)//链星
{
    pic[cnt].to=t;
    pic[cnt].nxt=head[f];
    pic[cnt].cot=c;
    head[f]=cnt;
    cnt++;
}
void dfs(int root)
{
    for(int i=head[root];i!=-1;i=pic[i].nxt) 
    {
        int to=pic[i].to;
        dfs(to);
        siz[root]+=siz[to];//更新siz
        for(int j=siz[root];j>=0;j--)//更新dp
        {
            for(int k=min(siz[to],j);k>=0;k--)
            {
                if(dp[root][j-k]!=-(1<<30))
                {
                    dp[root][j]=max(dp[root][j],dp[root][j-k]+dp[to][k]-pic[i].cot);
                }
            }
        }
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n-m;i++)
    {
        scanf("%d",&p0);
        for(int j=1;j<=p0;j++)
        {
            scanf("%d%d",&p1,&p2);
            add(i,p1,p2);
        }
    }
    for(int i=1;i<=n;i++)//不要滥用memset
    {
        for(int j=1;j<=n;j++)//j不能从0枚举
        {
            dp[i][j]=-(1<<30);
        }
    }
    for(int i=n-m+1;i<=n;i++)
    {
        scanf("%d",&dp[i][1]);
        siz[i]=1;//siz存储叶子数目,因而其他的不用赋成1
    }
    dfs(1);
    for(int i=m;i>=0;i--)
    {
        if(dp[1][i]>=0)
        {
            printf("%d",i);
            return 0;
        }
    }
    printf("0");
    return 0;
}

实际上,本蒟蒻第一次交的时候TLE找不出原因,后来发现实际是调试时把dp改小而导致RE,所以再郑重提醒大家一次,一定要在交码前确认删净了调试代码,把数组改回来

蒟蒻写题解不易,如有问题敬请见谅

 

TELE poj1155 题解

标签:下标   int   class   pac   style   names   问题   for   i++   

原文地址:https://www.cnblogs.com/Grharris/p/10017408.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!