深探树形dp

  看到同学在写一道树形dp,好奇直接拿来写,发现很不简单。

如图,看上去是不是很像选课,没错这不是选课,升级版吧,多加了点东西罢了。简单却调了一晚上和一上午。

思路:很简单强联通分量+缩点+树形dp。直接莽啊,发现强联通分量不是很会求,码力不好一直调。然后开始缩点,这个缩点就分成的讲究了你咋么缩都行反正是一张无向图不过要注意最后图是一个连通图,每个节点都会直接和间接和0(人造源点相连。

然后树上dp即可。很简单。树上dp出锅了,一直调,然后改成二叉树dp,还是wa。

发现缩点GG了根本不能那样缩,然后考虑缩点的细节。然后实现码力好点就行了。简单。

#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=102;
int n,m;
int lin[maxn],ver[maxn],nex[maxn],len=0;
int v[maxn],w[maxn],dfn[maxn],low[maxn],c[maxn];
int st[1000],top=0,vis[maxn],num=0,cnt=0,ru[maxn];
int cv[maxn],cw[maxn],f[maxn][600],ls[maxn],rx[maxn];//左儿子,右兄弟
vector<int>q[maxn];
void add(int x,int y)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++num;
    st[++top]=x;vis[x]=1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(dfn[tn]==0)
        {
            tarjan(tn);
            low[x]=min(low[x],low[tn]);
        }
        else if(vis[tn]==1)low[x]=min(low[x],dfn[tn]);
    }
    if(dfn[x]==low[x])
    {
        cnt++;int y;
        do
        {
            y=st[top--];vis[y]=0;
            c[y]=cnt;q[cnt].push_back(y);
        }
        while(x!=y);
    }
}
void dfs(int i,int j)
{
    if(i==-1||j==0||f[i][j]>0)return;
    dfs(rx[i],j);
    f[i][j]=max(f[i][j],f[rx[i]>0?rx[i]:0][j]);
    int vx=j-cw[i];
    for(int k=0;k<=vx;k++)
    {
        dfs(ls[i],vx-k);
        dfs(rx[i],k);
        f[i][j]=max(f[i][j],f[ls[i]>0?ls[i]:0][vx-k]+f[rx[i]>0?rx[i]:0][k]+cv[i]);
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();cout<<n<<endl;
    for(int i=1;i<=n;i++)w[i]=read();
    for(int i=1;i<=n;i++)v[i]=read();
    for(int i=1;i<=n;i++){int x;x=read();add(x,i);}
    for(int i=1;i<=n;i++)if(dfn[i]==0)tarjan(i);
    /*for(int x=0;x<=n;x++)
    {
        for(int i=lin[x];i;i=nex[i])
        {
            int tn=ver[i];
            if(vis[x]==0)cv[c[x]]+=v[x],cw[c[x]]+=w[x];
            if(vis[tn]==0)cv[c[tn]]+=v[tn],cw[c[tn]]+=w[tn];
            vis[x]=1;vis[tn]=1;
            if(c[x]==c[tn]){if(ins[c[x]]==0)cadd(c[0],c[x]),ins[c[x]]=1;}
            cadd(c[x],c[tn]);
        }
    }*///预处理完成!开始树形dp, 书上dp出现失误不知道哪错了,多叉转二叉!
    memset(rx,-1,sizeof(rx));memset(ls,-1,sizeof(ls));
    for(int x=1;x<=n;x++){cw[c[x]]+=w[x];cv[c[x]]+=v[x];}
    for(int x=1;x<=cnt;x++)
    {
        for(int j=0;j<q[x].size();j++)
        {
            int te=q[x][j];
            for(int i=lin[te];i;i=nex[i])
            {
                int tn=ver[i];
                if(f[x][c[tn]]==0&&x!=c[tn])
                {
                    f[x][c[tn]]=1;
                    rx[c[tn]]=ls[x];
                    ls[x]=c[tn];
                    ru[c[tn]]++;
                }
            }
        }
    }
    for(int i=1;i<=cnt;i++)if(ru[i]==0)rx[i]=ls[0],ls[0]=i;
    memset(f,0,sizeof(f));
    //dfs(c[0],m);
    //printf("%d\n",f[c[0]][m]);
    cout<<cnt<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/chdy/p/10082247.html
今日推荐