P2014 [CTSC1997]选课
P2014 [CTSC1997]选课
Solution
树上背包模板题
因为有多节课是没有先修课的,所以并不是只有一棵树,用一个0号点作为没有先修课的课程的先修课,这样就合并成了一棵树,只要选取m+1个点(必选0)即可。
转移方程: dp[u][j] = max(dp[u][j - k] + dp[v][k],dp[u][j]);
01背包,所以j要倒序枚举
代码
#include <bits/stdc++.h>
using namespace std;
const int SZ = 320;
typedef long long ll;
int n,m,fist[SZ],temp;
int dp[SZ][SZ],b[SZ],a;//dp[i][j] 为在i为根的子树上选择j个节点的最大学分
struct zt
{
int v,nxt;
}line[SZ];
inline void add(int a,int b)
{
line[++ temp] = (zt){b,fist[a]};
fist[a] = temp;
}
inline int dfs(int u)
{
dp[u][1] = b[u];
for(int i = fist[u];i != -1;i = line[i].nxt)
{
int v = line[i].v;
dfs(v);
for(int j = m;j >= 2;j --)
for(int k = 1;k < j;k ++)
dp[u][j] = max(dp[u][j - k] + dp[v][k],dp[u][j]);
}
}
int main()
{
memset(fist,-1,sizeof(fist));
scanf("%d%d",&n,&m);
m ++;
for(int i = 1;i <= n;i ++)
{
scanf("%d%d",&a,&b[i]);
add(a,i);
}
dfs(0);
printf("%d\n",dp[0][m]);
return 0;
}
P1273 有线电视网
P1273 有线电视网
Solution
与上题类似,改为只选叶子节点,同时减去边的花费。
令dp[i][j]为以i为根的子树中选择j个叶子节点的最大收益。
转移方程:dp[u][j] = max(dp[u][j],dp[u][j - k] + dp[v][k] - line[i].w)
最终结果可能是负值,所以dp数组初始化为一个极小值。
代码
#include <bits/stdc++.h>
using namespace std;
const int SZ = 3000 + 20;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int n,m,fist[SZ],val[SZ],temp,dp[SZ][SZ];
struct zt
{
int v,nxt,w;
}line[SZ];
inline void add(int x,int y,int z)
{
line[++temp] = (zt){y,fist[x],z};
fist[x] = temp;
}
inline int dfs(int u)
{
if(u > n - m) // 叶子
{
dp[u][1] = val[u];
return 1;
}
int sum = 0; // 当前节点为根时,子树中叶子节点的数量
for(int i = fist[u];i != -1;i = line[i].nxt)
{
int v = line[i].v;
int t = dfs(v);
sum += t;
for(int j = sum;j >= 1;j --)
for(int k = 1;k <= t;k ++)
if(j - k >= 0) dp[u][j] = max(dp[u][j - k] + dp[v][k] - line[i].w,dp[u][j]);
}
return sum;
}
int main()
{
memset(fist,-1,sizeof(fist));
memset(dp,~0x3f,sizeof(dp));
scanf("%d%d",&n,&m);
for(int i = 1;i <= n - m;i ++)
{
int k,a,b;
scanf("%d",&k);
for(int j = 1;j <= k;j ++)
{
scanf("%d%d",&a,&b);
add(i,a,b);
}
}
for(int i = n - m + 1;i <= n;i ++) scanf("%d",&val[i]);
for(int i = 1;i <= n;i ++) dp[i][0] = 0;
dfs(1);
for(int i = m;i >= 0;i --)
{
if(dp[1][i] >= 0)
{
printf("%d\n",i);
break;
}
}
return 0;
}
2020.7.12