再探树形dp

随着校oj终于刷进了第一页,可以不用去写那些水题了,开始认真学习自己的东西,当然包括文化课。努力。。

这道题呢是道树形dp,可看到了根本就不知道怎么写思考过程:

5min 终于看懂了题 画了样例的图把输出看懂了 然后发现这不可做。。

设个状态吧,这肯定是从子树上进行转移的然后然后f[i]表示以i为根节点子树的大小吧。然后真的就不可做了。

想列状态转移方程发现价值算不出来放弃,脑抽没有多加一维状态来表示价值哎。

无奈点开题解 1min恍然大悟。。dp好难。

其实这道题就是一个简单的树形背包dp,细节处理的不多。可是真的不好想。

#include<iostream>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<vector>
#include<stack>
#include<cstdio>
#include<map>
#include<deque>
#include<set>
#define inf 1000000000
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int maxn=200;
int n,m,ru[maxn];
int lin[maxn<<1],ver[maxn<<1],nex[maxn<<1],len=0;
int vis[maxn],ans=inf,f[maxn][maxn];//f[i][j]表示第i个节点保留j条边需要截下的最小代价
void add(int x,int y)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
}
void dfs(int x)
{
    vis[x]=1;
    f[x][1]=ru[x];//只保留x节点的话需要砍ru[x]条边
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn]==1)continue;
        dfs(tn);
        for(int j=m;j>=1;j--)//01背包模型
            for(int k=1;k<j;k++)
                f[x][j]=min(f[x][j],f[x][k]+f[tn][j-k]-2);
            //-2原因为现在加上儿子节点的这么多边后可以少砍f[x][k]少砍1条,f[tn][j-k]少砍一条
    }
}
int main()
{
    freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<n;i++)
    {
        int x,y;
        x=read();y=read();
        add(x,y);add(y,x);
        ru[x]++;ru[y]++;
    }
    memset(f,10,sizeof(f));
    dfs(1);
    for(int i=1;i<=n;i++)ans=min(ans,f[i][m]);
    printf("%d\n",ans);
    return 0;
}
View Code

一中午时间学习了一下树形dp的二次扫描+换根,觉得不算是很难,dp转移方程也很好推就是有点绕。

主要是一个求出最大的源点能发出的水量 poj3585

扫描二维码关注公众号,回复: 4397560 查看本文章

n的范围20000 直接O(n^2)爆力直接超时,考虑O(n)求出。设d[i]表示以i为节点从i的子树身上所能的到的最大水量。

则有d[x]+=((ru[tn]==1)?e[i]:min(d[tn],e[i]));直接在O(n)时间之内求出d数组,这也就是这道题的难点了,我们不知道跟在哪,所以外面加上一层for循环的话直接就是O(n)的了,考虑怎么搞出来全部点。设f[i]表示以i为根节点所能得到的最大值,随便设root=1;先假设root为根,把d数组跑出来。

之后求f数组就行了,显然f[root]=d[root];然后通过这一点进行对f数组进行更新。

看完书后得到f[tn]=d[tn]+((ru[x]==1)?e[i]:min(f[x]-min(d[tn],e[i]),e[i]));看图理解的快很神奇就实现了换根!

于是有代码:

#include<iostream>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<vector>
#include<stack>
#include<cstdio>
#include<map>
#include<deque>
#include<set>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int maxn=200002;
int t,n;
int ver[maxn<<1],lin[maxn<<1],nex[maxn<<1],e[maxn<<1],len=0;
int vis[maxn],d[maxn],ru[maxn],f[maxn],root,ans=0;
void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
void dp(int x)
{
    vis[x]=1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn]==1)continue;
        dp(tn);
        if(ru[tn]==1)d[x]+=e[i];
        else d[x]+=min(d[tn],e[i]);
    }
}
void dfs(int x)
{
    vis[x]=1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn]==1)continue;
        if(ru[x]==1)f[tn]=d[tn]+e[i];
        else f[tn]=d[tn]+min(f[x]-min(d[tn],e[i]),e[i]);
        dfs(tn);
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    t=read();
    while(t--)
    {
        memset(lin,0,sizeof(lin));
        memset(e,0,sizeof(e));
        memset(ru,0,sizeof(ru));
        memset(d,0,sizeof(d));
        memset(f,0,sizeof(f));
        ans=0;root=1;len=0;
        n=read();
        for(int i=1;i<n;i++)
        {
            int x,y,z;
            x=read();y=read();z=read();
            add(x,y,z);add(y,x,z);
            ru[x]++;ru[y]++;
        }
        memset(vis,0,sizeof(vis));
        dp(root);
        memset(vis,0,sizeof(vis));
        f[root]=d[root];
        dfs(root);
        for(int i=1;i<=n;i++)ans=max(ans,f[i]);
        printf("%d\n",ans);
    }
    return 0;
}
View Code

成就感足足的。

且放白鹿青崖间。

猜你喜欢

转载自www.cnblogs.com/chdy/p/10078625.html