某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。
从转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总费用等于传输信号的费用总和。
现在每个用户都准备了一笔费用想观看这场精彩的足球比赛,有线电视网有权决定给哪些用户提供信号而不给哪些用户提供信号。
写一个程序找出一个方案使得有线电视网在不亏本的情况下使观看转播的用户尽可能多。
输入格式:
输入文件的第一行包含两个用空格隔开的整数N和M,其中2≤N≤3000,1≤M≤N-1,N为整个有线电视网的结点总数,M为用户终端的数量。
第一个转播站即树的根结点编号为1,其他的转播站编号为2到N-M,用户终端编号为N-M+1到N。
接下来的N-M行每行表示—个转播站的数据,第i+1行表示第i个转播站的数据,其格式如下:
K A1 C1 A2 C2 … Ak Ck
K表示该转播站下接K个结点(转播站或用户),每个结点对应一对整数A与C,A表示结点编号,C表示从当前转播站传输信号到结点A的费用。最后一行依次表示所有用户为观看比赛而准备支付的钱数。
输出格式:
输出文件仅一行,包含一个整数,表示上述问题所要求的最大用户数。
挺水的一道树形DP,但依然调了半个多小时(转移处多-1了然后样例过不去)。多叉树太难写了我转成了二叉树,边权转化点权。
f【i】【j】表示i为根,播j个用户的最大收益。答案倒序遍历一遍就好了,dp时有许多小细节。
要想清楚转移的条件(有儿子,选不选自己之类的)代码里有注释。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
const int N=3005;
const int INF=1e9;
struct node{
int lson,rson;
}node[MAXN];
int head[MAXN],mon[MAXN],size[MAXN],ls[MAXN<<2],rs[MAXN<<2],cnt=0;
int n,m,f[N][N];
inline void add(int u,int v){
if(!node[u].lson){
node[u].lson=v;
}
else {
node[v].rson=node[u].lson;
node[u].lson=v;
}
}
//f[i][j]表示以i为根节点 给j个用户弄的最大收益.
void dfs1(int u){
f[u][0]=0;
if(node[u].lson){
dfs1(node[u].lson);
size[u]+=size[node[u].lson];
}
if(node[u].rson){
dfs1(node[u].rson);
size[u]+=size[node[u].rson];
}
}
void dp(int u){
if(node[u].lson){
dp(node[u].lson);
for(int i=1;i<=size[u];i++){//一共选i个
for(int j=0;j<=i;j++){//在左子树里选j个 左子树选了自己也一定选 (用这个节点原来的儿子)
if(!node[u].lson)continue;
if(j>size[node[u].lson])continue;
if(i-j>size[node[u].rson])continue;
f[u][i]=max(f[u][i],f[node[u].lson][j]+f[node[u].rson][i-j]+mon[u]);//选自己
}
if(!node[u].lson&&node[u].rson&&i-1<=size[node[u].rson])
f[u][i]=max(f[u][i],f[node[u].rson][i-1]+mon[u]);//(这个节点就是用户,用他兄弟和他)
if(node[u].rson&&i<=size[node[u].rson])
f[u][i]=max(f[u][i],f[node[u].rson][i]); //(只用他的兄弟)
}
}
if(node[u].rson){
dp(node[u].rson);
for(int i=1;i<=size[u];i++){//一共选i个
for(int j=0;j<=i;j++){//在左子树里选j个 左子树选了自己也一定选 (用这个节点原来的儿子)
if(!node[u].lson)continue;
if(j>size[node[u].lson])continue;
if(i-j>size[node[u].rson])continue;
f[u][i]=max(f[u][i],f[node[u].lson][j]+f[node[u].rson][i-j]+mon[u]);//选自己
}
if(!node[u].lson&&node[u].rson&&i-1<=size[node[u].rson])
f[u][i]=max(f[u][i],f[node[u].rson][i-1]+mon[u]);//(这个节点就是用户,用他兄弟和他)
if(node[u].rson&&i<=size[node[u].rson])
f[u][i]=max(f[u][i],f[node[u].rson][i]); //(只用他的兄弟)
}
}
}
int main(){
int k,nod,w;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)f[i][j]=-INF;
for(int i=1;i<=n-m;i++){
scanf("%d",&k);
for(int j=1;j<=k;j++){
scanf("%d%d",&nod,&w);
add(i,nod);
mon[nod]-=w;
}
}
for(int i=n-m+1;i<=n;i++){
int tem2;
scanf("%d",&tem2);
mon[i]+=tem2;
f[i][1]=mon[i];
size[i]=1;
}
dfs1(1);
dp(1);
int ans=0;
for(int i=m;i>=0;i--){
if(f[1][i]>=0){
ans=i;break;
}
}
printf("%d\n",ans);
return 0;
}