菜菜推荐的“水题”虐了我一天T T...(菜菜好强强qwq~
显然是个分数规划题,二分答案算出p[i]-mid*s[i]之后在树上跑依赖背包,选k个最大值如果>0说明还有更优解。
第一次接触树形依赖背包,所以之前写的十几发WA和TLE都是错误写法,我还是naive啊T T
树形依赖背包的普遍做法是按dfs序DP,设f[i][j]为dfs序为i的点,已经选了j个点的最大价值,nxt[i]为i的下一个子树的dfs序则有:
f[nxt[i]][j]=f[i][j]
f[i+1][j+1]=f[i][j]+w[i]
注意树形依赖背包最好使用刷表法,因为如果要求代价必须为k并且权值有负数的话使用填表法可能会导致从不合法状态转移。
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int maxn=2510,inf=1e9; struct poi{int too,pre;}e[maxn]; int n,k,x,tot,cnt,mx; int dfn[maxn],nxt[maxn],last[maxn],s[maxn],p[maxn]; double f[maxn][maxn],w[maxn]; inline void read(int &k) { int f=1;k=0;char c=getchar(); while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar(); while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar(); k*=f; } void add(int x,int y){e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;} void dfs(int x) { dfn[x]=cnt++; for(int i=last[x];i;i=e[i].pre)dfs(e[i].too); nxt[dfn[x]]=cnt; } int main() { read(k);read(n); for(int i=1;i<=n;i++)read(s[i]),read(p[i]),read(x),add(x,i),mx=max(mx,p[i]); dfs(0); double l=0,r=1e4; while(r-l>1e-5) { double mid=(l+r)/2; for(int i=1;i<=n;i++)w[dfn[i]]=1.0*p[i]-mid*s[i]; for(int i=1;i<=n+1;i++)for(int j=0;j<=k+1;j++)f[i][j]=-inf; for(int i=0;i<=n;i++) for(int j=0;j<=min(i,k+1);j++) { if(f[i][j]>f[nxt[i]][j])f[nxt[i]][j]=f[i][j]; if(f[i][j]+w[i]>f[i+1][j+1])f[i+1][j+1]=f[i][j]+w[i]; } if(f[n+1][k+1]>1e-5)l=mid;else r=mid; } printf("%.3lf\n",l); }