天天看點

The more, The Better HDU - 1561

​​http://acm.hdu.edu.cn/showproblem.php?pid=1561​​

先正向拓撲去掉無法到達的點 然後用剩下的點建一正一反兩個圖

然後逆向拓撲 當某點因度數為0而入隊列時 說明正向圖中他的所有後繼節點(即從該點進行BFS能到達的所有點)都已周遊 這和樹型DP的思想一樣 隻不過這裡需要考慮的拓撲序稍複雜一些

這時可以想到利用01分組背包的思想 dp[i][j]代表從第i個節點出發攻占至多j個節點的最大收益 即對要入隊列的節點v 周遊其正向圖中所有後繼節點w 每個節點w的dp[w][1] dp[w][2]...dp[w][m]都當做一樣物品且每樣隻有一件 這就是01背包 又因為每樣物品互相沖突 這又是分組背包

還有要注意的是 因為除了虛根0之外 每個節點v在計算dp[v][i]時都要保證包含本身節點 詳見代碼

最後 我孫雨田現在也能寫出正兒八經的DP題了!!!(發現其實是自己想麻煩了而已)

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e2+10;

struct node
{
    int v,next;
};

queue <int> que;
node edge1[maxn],edge2[maxn];
int dp[maxn][maxn];
int first1[maxn],first2[maxn],degree[maxn],pre[maxn],val[maxn],book[maxn];
int n,m,num1,num2;

void addedge(node* edge,int* first,int u,int v,int &num)
{
    edge[num].v=v;
    edge[num].next=first[u];
    first[u]=num++;
}

void toposort()
{
    int i,u,v;
    while(!que.empty()) que.pop();
    memset(book,0,sizeof(book));
    que.push(0);
    book[0]=1;
    while(!que.empty()){
        u=que.front();
        que.pop();
        for(i=first1[u];i!=-1;i=edge1[i].next){
            v=edge1[i].v;
            degree[v]--;
            if(degree[v]==0){
                que.push(v);
                book[v]=1;
            }
        }
    }
}

void rebuild()
{
    int u;
    memset(first1,-1,sizeof(first1));
    memset(first2,-1,sizeof(first2));
    memset(degree,0,sizeof(degree));
    num1=0,num2=0;
    for(u=1;u<=n;u++){
        if(book[u]&&book[pre[u]]){
            addedge(edge1,first1,pre[u],u,num1);
            addedge(edge2,first2,u,pre[u],num2);
            degree[pre[u]]++;
        }
    }
}

int solve()
{
    int tmp[maxn];
    int i,j,k,l,p,u,v,w;
    memset(dp,0,sizeof(dp));
    while(!que.empty()) que.pop();
    for(i=1;i<=n;i++){
        if(degree[i]==0){
            que.push(i);
            for(j=1;j<=m;j++){
                dp[i][j]=val[i];
            }
        }
    }
    while(!que.empty()){
        u=que.front();
        que.pop();
        for(i=first2[u];i!=-1;i=edge2[i].next){
            v=edge2[i].v;
            degree[v]--;
            if(degree[v]==0){
                //printf("***%d***\n",v);
                que.push(v);
                for(l=1;l<=m;l++){
                    dp[v][l]=val[v];
                }
                for(j=first1[v];j!=-1;j=edge1[j].next){
                    w=edge1[j].v;
                    //printf("*%d*\n",w);
                    for(l=1;l<=m;l++) tmp[l]=dp[v][l];
                    //for(l=1;l<=m;l++) printf("%d ",tmp[l]);
                    //printf("\n");
                    for(k=1;k<=m;k++){
                        //printf("^%d^\n",k);
                        if(v==0) p=k;
                        else p=k+1;
                        for(l=m;l>=p;l--){
                            //printf("%d %d\n",dp[v][l-k],dp[w][k]);
                            tmp[l]=max(tmp[l],dp[v][l-k]+dp[w][k]);
                        }
                    }
                    //for(l=1;l<=m;l++) printf("%d ",tmp[l]);
                    //printf("\n");
                    for(l=1;l<=m;l++) dp[v][l]=max(dp[v][l],tmp[l]);
                }

            }
        }
    }
    /*
    for(i=0;i<=n;i++){
        printf("***%d***\n",i);
        for(j=0;j<=m;j++){
            printf("%d ",dp[i][j]);
        }
        printf("\n");
    }
    */
    return dp[0][m];
}

int main()
{
    int u;
    while(scanf("%d%d",&n,&m)!=EOF){
        if(n==0&&m==0) break;
        memset(first1,-1,sizeof(first1));
        memset(degree,0,sizeof(degree));
        num1=0;
        for(u=1;u<=n;u++){
            scanf("%d%d",&pre[u],&val[u]);
            addedge(edge1,first1,pre[u],u,num1);
            degree[u]++;
        }
        toposort();
        rebuild();
        printf("%d\n",solve());
    }
    return 0;
}