菜菜推荐的“水题”虐了我一天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);
}
View Code

相关文章:

  • 2022-01-31
  • 2021-11-27
  • 2021-09-16
  • 2021-10-07
  • 2021-09-09
  • 2021-08-25
  • 2021-12-04
  • 2022-03-03
猜你喜欢
  • 2022-12-23
  • 2021-09-11
  • 2022-12-23
  • 2021-08-20
  • 2021-09-17
  • 2021-09-27
  • 2021-11-04
相关资源
相似解决方案