内网 可怜与超市题解 树形dp+合并
调过了题比较高兴
首先想到了dp柿子 f[i][j][0/1]代表第i个节点买了j个用(1)没用(0)优惠券的最小花费。
而且是从子节点向父节点转移。
f[i][j][0]=min(f[k][l][0]+f[i][j-l][0]) k是i的儿子,l属于sz[i]。
f[i][j][1]=min(min(f[k][l][1],f[k][l][0])+f[i][j-l][1]) 同上。
然后暴力转移,发现是n3复杂度,最多T70(再次%%%有钱人用我的柿子暴力卡过)
有钱人的暴力方法:判断当前钱数是否超过了所带的钱数,超过根本无法转移。
其实这个卡常我也想到了但是没有打全因为懒话说打正解不是更麻烦么嘤嘤嘤
说一下正解
运用到了学长在写考试T2时的思想,也就是合并,具体证明参见skyh博客%%%%%
然后我们发现它也可以合并(估计学长就是想让我们练这个)
tmp数组1维滚动,2维代表用没用优惠券,3维代表买了几个 (两个半维)
先把自己加上去,sz++,然后tmp赋初值。
接下来枚举儿子,让当前大小j和儿子大小k共同去更新j+k,在更新完之后去加上儿子的大小
所以复杂度会从n3下降到n2,最后更新答案。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define ll long long 5 using namespace std; 6 int const maxn=5010; 7 int const inf=0x3f3f3f3f; 8 struct node{int to,nxt;}l[5010]; 9 int n,b,tot,head[maxn],c[maxn],d[maxn],fa[maxn],sz[maxn],f[maxn][maxn][2],ans,tmp[2][2][maxn]; 10 void add(int x,int y) 11 { 12 l[++tot].to=y; 13 l[tot].nxt=head[x]; 14 head[x]=tot; 15 } 16 void dfs(int x) 17 { 18 sz[x]=1; 19 for(int i=head[x];i;i=l[i].nxt) 20 { 21 dfs(l[i].to); 22 sz[x]+=sz[l[i].to]; 23 } 24 for(int i=0;i<=sz[x];i++) f[x][i][0]=f[x][i][1]=inf; 25 if(sz[x]==1) 26 { 27 f[x][0][0]=0;f[x][1][0]=c[x];f[x][1][1]=c[x]-d[x]; 28 return ; 29 } 30 memset(tmp,0x3f,sizeof(tmp)); 31 int tm=0,size=1,cur=0; 32 tmp[0][0][0]=0;tmp[0][0][1]=c[x];tmp[0][1][1]=c[x]-d[x]; 33 for(int i=head[x];i;i=l[i].nxt) 34 { 35 int y=l[i].to;tm++; 36 memset(tmp[cur^1],0x3f,sizeof(tmp[cur^1])); 37 for(int j=size;j>=0;j--) 38 { 39 for(int k=0;k<=sz[y];k++) 40 { 41 tmp[cur^1][0][j+k]=min(tmp[cur^1][0][j+k],min(tmp[cur][0][j]+f[y][k][0],tmp[cur][0][j+k])); 42 if(j!=0) tmp[cur^1][1][j+k]=min(tmp[cur^1][1][j+k],min(tmp[cur][1][j]+min(f[y][k][0],f[y][k][1]),tmp[cur][1][j+k])); 43 } 44 } 45 cur^=1;size+=sz[y]; 46 } 47 for(int i=1;i<=sz[x];i++) f[x][i][0]=tmp[cur][0][i],f[x][i][1]=tmp[cur][1][i]; 48 } 49 int main() 50 { 51 scanf("%d%d",&n,&b); 52 scanf("%d%d",&c[1],&d[1]); 53 memset(f,0x3f,sizeof(f)); 54 for(int i=2;i<=n;i++) 55 { 56 scanf("%d%d%d",&c[i],&d[i],&fa[i]); 57 add(fa[i],i); 58 } 59 dfs(1); 60 for(int i=0;i<=n;i++) 61 { 62 if(f[1][i][0]<=b) ans=max(ans,i); 63 if(f[1][i][1]<=b) ans=max(ans,i); 64 } 65 printf("%d",ans); 66 return 0; 67 }
我有必须去做的理由。