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;
}

猜你喜欢

转载自blog.csdn.net/sunyutian1998/article/details/89343487