for(int i=k;i>=w[x];i--)
for(int j=0;j<=k;j++){
if(i+j>k) break;
f[x][i+j]=max(f[x][i+j],f[x][i]+f[son][j])
}
f[x][w[x]]=v[x]
,并且在第一维枚举容量时只枚举到w[x]
。for(int i=siz[x];i>=1;i--)
for(int j=0;j<=siz[son];j++){
if(i+j>k) break;
f[x][i+j]=max(f[x][i+j],f[x][i]+f[son][j])
}
siz[x]=1
,然后每次枚举完一个儿子的时候,就执行语句siz[x]+=siz[son]
。这样可以大大降低复杂度,有巨佬指出,原来不进行优化的时间复杂度为 O ( n 3 ) O(n^3) O(n3),但是进行了这个 s i z siz siz数组的优化后,可以接近于 O ( n 2 ) O(n^2) O(n2)这也就是为什么许多 n n n为 2500 2500 2500的题目,也可以用树形依赖背包来做。题目描述
在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b)。一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少?
输入输出格式
输入格式:
第一行有两个整数N,M用空格隔开。(1<=N<=300,1<=M<=300)
接下来的N行,第I+1行包含两个整数ki和si, ki表示第I门课的直接先修课,si表示第I门课的学分。若ki=0表示没有直接先修课(1<=ki<=N, 1<=si<=20)。
输出格式:
只有一行,选M门课程的最大得分。
输入输出样例
输入样例#1:
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
输出样例#1:
13
#include
#define MAXN 205
using namespace std;
int read(){
char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,m,cnt,siz[MAXN],head[MAXN],nxt[MAXN],f[MAXN][105];
struct node{
int to,val;
}L[MAXN];
void add(int x,int y,int c){
L[cnt]=(node){y,c};
nxt[cnt]=head[x];head[x]=cnt;cnt++;
}
void get(int x){
siz[x]=1;
for(int i=head[x];i!=-1;i=nxt[i]){
int to=L[i].to;
get(to);siz[x]+=siz[to];
}
}
int dfs(int x){
for(int i=head[x];i!=-1;i=nxt[i]){
int to=L[i].to;dfs(to);
for(int j=siz[x]-1;j>=0;j--)
for(int k=0;k<=siz[to]-1;k++)
if(j>k) f[x][j]=max(f[x][j],f[x][j-k-1]+L[i].val+f[to][k]);
}
}
int main()
{
n=read();m=read();
memset(head,-1,sizeof(head));
memset(f,~0x3f,sizeof(f));
for(int i=1;i<=n;i++){
int x=read()+1,c=read();
add(x,i+1,c);
}
for(int i=1;i<=n+1;i++) f[i][0]=0;
get(1);dfs(1);
printf("%d",f[1][m]);
return 0;
}
#include
#define MAXN 3005
using namespace std;
int read(){
char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,m,cnt,ans,head[MAXN],nxt[MAXN],go[MAXN],dp[MAXN][MAXN],w[MAXN],son[MAXN],siz[MAXN];
struct node{
int to,val;
}L[MAXN];
void add(int x,int y,int c){
L[cnt]=(node){y,c};
nxt[cnt]=head[x];head[x]=cnt;cnt++;
}
int dfs(int x){ //这个操作很silly,大家千万不要学,又慢又长。直接在转移时更新既短又快
siz[x]=1;
for(int i=head[x];i!=-1;i=nxt[i]){
int to=L[i].to;
siz[x]+=dfs(to);
}
return siz[x];
}
void calc(int x){
if(x>n-m){dp[x][1]=w[x];return;}
for(int i=head[x];i!=-1;i=nxt[i]){
int to=L[i].to;calc(to);
for(int j=siz[x];j>=0;j--)
for(int k=1;k<=siz[to];k++)
if(j>=k) dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[to][k]-L[i].val);
}
}
int main()
{
n=read();m=read();
memset(head,-1,sizeof(head));
memset(dp,~0x3f,sizeof(dp));
for(int i=1;i<=n;i++) dp[i][0]=0;
for(int i=1;i<=n-m;i++){
son[i]=read();
for(int j=1;j<=son[i];j++){
int x=read(),y=read();add(i,x,y);
}
}
for(int i=1;i<=m;i++) w[n-m+i]=read();
dfs(1);calc(1);
for(int i=0;i<=n;i++) if(dp[1][i]>=0) ans=i;
printf("%d",ans);
return 0;
}
#include
#define MAXN 5005
#define ll long long
using namespace std;
ll read(){
char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
ll n,m,ans,flag,v[MAXN],dc[MAXN],f[MAXN][MAXN],g[MAXN][MAXN];
ll cnt,head[MAXN<<1],nxt[MAXN<<1],go[MAXN<<1],siz[MAXN];
void add(ll x,ll y){
go[cnt]=y;nxt[cnt]=head[x];head[x]=cnt;cnt++;
}
void dfs(ll x){
register int i,j,k;
g[x][0]=0;f[x][1]=v[x]-dc[x];g[x][1]=v[x];siz[x]=1;
for(k=head[x];k!=-1;k=nxt[k]){
ll to=go[k];dfs(to);
for(i=siz[x];i>=0;i--)
for(j=1;j<=siz[to];j++)
g[x][i+j]=min(g[x][i+j],g[x][i]+g[to][j]);
for(i=siz[x];i>=1;i--)
for(j=1;j<=siz[to];j++)
f[x][i+j]=min(f[x][i+j],f[x][i]+min(f[to][j],g[to][j]));
siz[x]+=siz[to];
}
}
int main()
{
n=read();m=read();register int i;
memset(head,-1,sizeof(head));
memset(f,127,sizeof(f));
memset(g,127,sizeof(g));
for(i=1;i<=n;i++){
v[i]=read();dc[i]=read();
if(i>1){ll x=read();add(x,i);}
}
dfs(1);
for(i=n;i;i--)
if(f[1][i]<=m||g[1][i]<=m){flag=1;printf("%d",i);break;}
if(!flag) puts("0");
return 0;
}
Description
现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。
但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。
我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。
Input
第1行:N, M (0<=N<=100, 0<=M<=500)
第2行:W1, W2, … Wi, …, Wn (0<=Wi<=M )
第3行:V1, V2, …, Vi, …, Vn (0<=Vi<=1000 )
第4行:D1, D2, …, Di, …, Dn (0<=Di<=N, Di≠i )
Output
一个整数,代表最大价值。
Sample Input
3 10
5 5 6
2 3 4
0 1 1
Sample Output
5
#include
#define MAXN 505
using namespace std;
int read(){
char c;int x=0,y=1;while(c=getchar(),(c<'0'||c>'9')&&c!='-');
if(c=='-') y=-1;else x=c-'0';while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';return x*y;
}
int n,m,cnt,top,ans,w[MAXN],v[MAXN],head[MAXN<<1],nxt[MAXN<<1],go[MAXN<<1],in[MAXN];
int ta,co,dfn[MAXN],low[MAXN],vis[MAXN],sta[MAXN],col[MAXN],fa[MAXN],f[MAXN][505];
vector g[MAXN];
struct node{
int w,v;
}F[MAXN];
void add(int x,int y){
go[cnt]=y;nxt[cnt]=head[x];head[x]=cnt;cnt++;
}
void tarjan(int x){
dfn[x]=low[x]=++ta;
vis[x]=1;sta[++top]=x;
for(int i=head[x];i!=-1;i=nxt[i]){
int to=go[i];
if(!dfn[to]) tarjan(to),low[x]=min(low[x],low[to]);
else if(vis[to]) low[x]=min(low[x],dfn[to]);
}
if(dfn[x]==low[x]){
co++;int now=0;
while(now!=x){
now=sta[top--];
vis[now]=0;col[now]=co;
F[co].w+=w[now];
F[co].v+=v[now];
}
}
}
int find(int x){
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
void unionn(int x,int y){
x=find(x);y=find(y);
fa[y]=x;
}
void dfs(int x){
if(F[x].w>m) return;
f[x][F[x].w]=F[x].v;
for(int i=0;i=F[x].w;j--)
for(int k=0;k<=m;k++){
if(j+k>m) break;
f[x][j+k]=max(f[x][j+k],f[x][j]+f[to][k]);
}
}
}
int main()
{
n=read();m=read();
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++) w[i]=read();
for(int i=1;i<=n;i++) v[i]=read();
for(int i=1;i<=n;i++){
int x=read();
if (x)add(x,i);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
for(int i=1;i<=co;i++) fa[i]=i;
for(int x=1;x<=n;x++)
for(int i=head[x];i!=-1;i=nxt[i]){
int to=go[i];
if(find(col[x])!=find(col[to])){
unionn(col[x],col[to]);in[col[to]]++;
g[col[x]].push_back(col[to]);
}
}
for(int i=1;i<=co;i++)
if(!in[i]) g[0].push_back(i);
F[0]=(node){0,0};
dfs(0);
for(int i=0;i<=m;i++) ans=max(ans,f[0][i]);
printf("%d",ans);
return 0;
}
Description
JSOI信息学代表队一共有N名候选人,这些候选人从1到N编号。方便起见,JYY的编号是0号。每个候选人都由一位
编号比他小的候选人Ri推荐。如果Ri=0则说明这个候选人是JYY自己看上的。为了保证团队的和谐,JYY需要保证,
如果招募了候选人i,那么候选人Ri"也一定需要在团队中。当然了,JYY自己总是在团队里的。每一个候选人都有
一个战斗值Pi",也有一个招募费用Si"。JYY希望招募K个候选人(JYY自己不算),组成一个性价比最高的团队。
也就是,这K个被JYY选择的候选人的总战斗值与总招募总费用的比值最大。
Input
输入一行包含两个正整数K和N。
接下来N行,其中第i行包含3个整数Si,Pi,Ri表示候选人i的招募费用,战斗值和推荐人编号。
对于100%的数据满足1≤K≤N≤2500,0<"Si,Pi"≤10^4,0≤Ri
Output
输出一行一个实数,表示最佳比值。答案保留三位小数。
Sample Input
1 2
1000 1 0
1 1000 1
Sample Output
0.001
a[i]-x*b[i]
,然后进行树形依赖背包,求出选中 k k k个人之后的最大价值,如果最大价值大于 0 0 0,那么也就是 c h e c k check check满足条件。#include
#define MAXN 2550
#define db double
#define eps 1e-6
using namespace std;
int read(){
char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,k,cnt,a[MAXN],b[MAXN],siz[MAXN];
int head[MAXN<<1],nxt[MAXN<<1],go[MAXN<<1];
db l,r,mid,ans,v[MAXN],f[MAXN][MAXN];
void add(int x,int y){
go[cnt]=y;nxt[cnt]=head[x];head[x]=cnt;cnt++;
}
void dfs(int x){
f[x][1]=v[x];siz[x]=1;
for(int i=2;i<=k;i++) f[x][i]=-2147483647;
for(int i=head[x];i!=-1;i=nxt[i]){
int to=go[i];dfs(to);
for(int j=siz[x];j>=1;j--)
for(int p=0;p<=siz[to];p++){
if(j+p>k) break;
f[x][j+p]=max(f[x][j+p],f[x][j]+f[to][p]);
}
siz[x]+=siz[to];
}
}
int main()
{
k=read();n=read();k++;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++){
b[i]=read();a[i]=read();
int x=read();add(x,i);
}
l=0;r=1000000;
while(l<=r){
mid=(l+r)/2;
for(int i=1;i<=n;i++) v[i]=1.0*a[i]-mid*b[i];
dfs(0);
if(f[0][k]>=0) ans=mid,l=mid+eps;
else r=mid-eps;
}
printf("%.3f",ans);
return 0;
}
Description
有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。
问收益最大值是多少。
Input
第一行两个整数N,K。
接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。
输入保证所有点之间是联通的。
N<=2000,0<=K<=N
Output
输出一个正整数,表示收益的最大值。
Sample Input
5 2
1 2 3
1 5 1
2 3 1
2 4 2
Sample Output
17
【样例解释】
将点1,2染黑就能获得最大收益。
#include
#define MAXN 2005
#define ll long long
using namespace std;
int read(){
char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,m,cnt,head[MAXN<<1],nxt[MAXN<<1];
int siz[MAXN],fa[MAXN];
ll f[MAXN][MAXN];
struct node{
int to,val;
}L[MAXN<<1];
void add(int x,int y,int c){
L[cnt]=(node){y,c};
nxt[cnt]=head[x];head[x]=cnt;cnt++;
L[cnt]=(node){x,c};
nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
void dp(int x,int fa){
siz[x]=1;
for(int i=head[x];i!=-1;i=nxt[i]){
int to=L[i].to;
if(to==fa) continue;dp(to,x);
for(int j=min(siz[x],m);j>=0;j--)
for(int k=min(siz[to],m);k>=0;k--){
f[x][j+k]=max(f[x][j+k],f[x][j]+f[to][k]+L[i].val*1ll*k*(m-k)+L[i].val*1ll*(siz[to]-k)*(n-m-(siz[to]-k)));
}
siz[x]+=siz[to];
}
}
int main()
{
n=read();m=read();
memset(head,-1,sizeof(head));
memset(f,-0x3f,sizeof(f));
for(int i=1;i<=n;i++) f[i][0]=f[i][1]=0;
for(int i=1;i