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