1. 程式人生 > >最佳團體(jsoi2016)——01分數規劃&&樹形dp

最佳團體(jsoi2016)——01分數規劃&&樹形dp

題目描述
JSOI 資訊學代表隊一共有 N 名候選人,這些候選人從 1 到 N 編號。方便起見,JYY 的編號是 0 號。每個候選人都由一位編號比他小的候選人 Ri 推薦。如果 Ri=0 則說明這個候選人是 JYY 自己看上的。為了保證團隊的和諧,JYY 需要保證,如果招募了候選人 i,那麼候選人 Ri” 也一定需要在團隊中。當然了,JYY 自己總是在團隊裡的。每一個候選人都有一個戰鬥值 Pi”,也有一個招募費用 Si”。JYY 希望招募 K 個候選人(JYY 自己不算),組成一個性價比最高的團隊。

也就是,這 K 個被 JYY 選擇的候選人的總戰鬥值與總招募總費用的比值最大。

輸入
輸入一行包含兩個正整數 K 和 N。

接下來 N 行,其中第 i 行包含 3 個整數 Si,Pi,Ri 表示候選人 i 的招募費用,戰鬥值和推薦人編號。

對於 100% 的資料滿足 1≤K≤N≤2500,0<”Si,Pi”≤10^4,0≤Ri<=i

輸出
輸出一行一個實數,表示最佳比值。答案保留三位小數。

樣例輸入
1 2
1000 1 0
1 1000 1
樣例輸出
0.001

題解
根據公式

i = 1 n p [ i ]
i = 1 n
s [ i ]
很容易想到用分數規劃來解決問題

二分 ans,樹形 dp 判斷是否符合即可

程式碼

#include<bits/stdc++.h>
#define eps 1e-4
using namespace std;
struct node{int to,nex;}e[5002];
int n,m,p[2502],s[2502],head[2502],cnt,siz[2502];
double d[2502],f[2502][2502],l,r;
void add(int s,int t){e[++cnt].to=t;e[cnt].nex=head[s];head[s]=cnt;}
void dfs(int x){
    if(x) f[x][1]=d[x];
    else f[x][0]=0;
    siz[x]=x?1:0;
    for(int k=head[x];k;k=e[k].nex){
        dfs(e[k].to);
        int len=x?1:0;
        for(int j=min(m,siz[x]);j>=len;j--)
            for(int l=1;l<=min(m-j,siz[e[k].to]);l++)
                f[x][j+l]=max(f[x][j+l],f[x][j]+f[e[k].to][l]);
        siz[x]+=siz[e[k].to];}
}
bool pd(double mid){
    for(int i=1;i<=n;i++) d[i]=(double)p[i]-mid*s[i];
    for(int i=0;i<=n;i++) for(int j=0;j<=m;j++) f[i][j]=-1e9;
    dfs(0);
    return f[0][m]>0;
}
int main(){
    cin>>m>>n;
    for(int i=1,t;i<=n;i++){
        scanf("%d%d%d",&s[i],&p[i],&t);
        r=max(r,(double)p[i]);
        add(t,i);}
    while(r-l>eps){
        double mid=(l+r)/2;
        if(pd(mid)) l=mid;
        else r=mid;}
    printf("%.3lf\n",l);
    return 0;
}