description
大学里实行学分。每门课程都有一定的学分,学生只要选修了这门课,并通过考核就能获得相应的学分。学生最后的学分是他各门课学分的总和。每个学生都要选择规定数量的课程。其中有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其他的一些课程的基础上才能选修。
例如,《剥皮术》就必须在选修了《屠龙术》后才能选修。
我们称《屠龙术》是《剥皮术》的先修课。
每门课的直接先修课最多之有一门。两门课也可能存在相同的先修课。
每门课都有一个课号,课号依次是1,2,3……。以下表为例说明。
课号 先修课号 学分
1 无 1
2 1 1
3 2 3
4 无 3
5 2 4
上表中1是2的先修课,即如果要选修2,则1必须已被选过。
同样,要选修3,那么1和2都一定被选修过。
每个学生可选的课程总数是一定的,请找出一种方案,使得到的总学分最多。
solution
考虑动态规划,按dfs顺序dp,设f[i][j]表示到dfs序为i的点,选了j个点,当前点可选可不选的最大贡献,每次可以转移到dfs序+1的点,也可以转移到dfs序+size的点,记录一下当前状态由哪个状态转移过来即可。
code
using namespace std;
int const mn=1000+9,inf=1e9+7;
int n,m,w[mn],a[mn],b[mn],size[mn],gra,begin[mn],to[mn],next[mn],f[mn][mn],
g[mn][mn],h[mn][mn];
void insert(int u,int v){
to[++gra]=v;
next[gra]=begin[u];
begin[u]=gra;
}
void dfs(int p){
b[++b[0]]=p;
size[p]=1;
fr(i,p){
dfs(to[i]);
size[p]+=size[to[i]];
}
}
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,n){
int fa;
scanf("%d%d",&fa,&w[i]);
if(fa)insert(fa,i);
else a[++a[0]]=i;
}
fo(i,1,a[0])dfs(a[i]);
fo(i,0,n)fo(j,0,m+1)f[i][j]=-inf;
f[1][0]=0;
fo(i,1,n)fo(j,0,m){
int p=b[i];
if(f[i+1][j+1]<f[i][j]+w[p]){
f[i+1][j+1]=f[i][j]+w[p];
g[i+1][j+1]=i;
h[i+1][j+1]=j;
}
if(f[i+size[p]][j]<f[i][j]){
f[i+size[p]][j]=f[i][j];
g[i+size[p]][j]=i;
h[i+size[p]][j]=j;
}
if(f[i+size[p]][j+1]<f[i][j]+w[p]){
f[i+size[p]][j+1]=f[i][j]+w[p];
g[i+size[p]][j+1]=i;
h[i+size[p]][j+1]=j;
}
}
printf("%d\n",f[n+1][m]);
if(m>=100)return 0;
a[0]=0;
int x=n+1,y=m;
while(g[x][y]){
int nx=g[x][y],ny=h[x][y];
if(y!=ny)a[++a[0]]=b[nx];
x=nx;y=ny;
}
sort(a+1,a+a[0]+1);
fo(i,1,a[0])printf("%d\n",a[i]);
return 0;
}