1. 程式人生 > >[JSOI2016]最佳團體

[JSOI2016]最佳團體

遍歷 pri 好的 子節點 大小 logs for 一道 return

傳送門

個人認為本題是這道題的加強版,加強之處在於本題要用到背包類樹形DP.所以不會背包類樹形DP的話,可以先做這道題.也可以自行學習.

推薦的第一道題的題解

推薦的第二道題的題解

題意:N個人,1-N編號,每個人價值pi,費用si,推薦人ri,如果選了i,就必須要選擇他的推薦人ri,ri可以等於零(編號為0的人是特殊的,它永遠在團隊中,且沒有價值和費用),從這N個人中選出m個人(不包括編號0的人)構成一個團隊,使得團隊總價值與總費用的比值最大.

又看到了比值最大(小)問題,就想到了分數規劃,理解清楚題意,列出我們要求的答案就是\(\frac{\sum_{i=1}^{k}pi}{\sum_{i=1}^{k}si}\)

想到了分數規劃,就想到了要二分答案,直接二分mid,如果此次二分的值成立,即\(\frac{\sum_{i=1}^{k}pi}{\sum_{i=1}^{k}si}>=mid\)

整理一下上式得\(\sum_{i=1}^{k} (pi-si*mid>=0)\)

所以每次check時,把每個人的價值更新為\(pi-si*mid\),題目就轉換為了有N個人,已知每個人的價值,選出k個人使得價值和>=0,有沒有看到背包的影子?

以上都是分數規劃的思路的模板,現在我們來考慮這道題目的特殊之處,就是每個人都有唯一的推薦人ri,只有推薦人ri被選中,才能選擇第i個人.

因為每個人都有唯一的推薦人ri,相當於樹中每個節點最多只有一個父節點,又因為有0號節點這個特殊的點存在,所以N+1個節點構成了一棵以0號節點為根節點的樹,還因為題目要求最優解,於是就想到了可以樹形DP.

設f[x][i]表示在以x為根節點的子樹中選擇i個點能獲得的最大價值(一般樹形DP都是這樣設的吧),不講了.

P.S.不知道是本題卡常太過分,還是我的代碼太醜,一定要吸氧才能不超時(這道題我交了幾十遍,最好的成績都T了一個點,歡迎各位不吸氧就能過的巨佬吊打教我)

int n,m;
int s[2505],p[2505],r[2505],size[2505];
double eps=1e-6;//設置二分精度
double v[2505],f[2505][2505];
vector<int> q[2505];
inline void dfs(int x){
    f[x][0]=0;f[x][1]=v[x];
    for(int i=2;i<=m;i++)f[x][i]=-1e9;
//初始化,這個很顯然吧
    size[x]=1;
//size數組表示以x為根節點的子樹中的子節點的數量
//把x視作以x為根節點的子樹中的子節點,所以初始值為1
//用於優化樹形DP,不優化T飛5個點
//不懂的話,底下推薦的第二篇博客有提到
    for(int i=0;i<q[x].size();i++){
        int y=q[x][i];
        dfs(y);
        for(int j=size[x];j>=1;j--)
            for(int k=0;k<=size[y];k++){
                if(j+k>m)break;//優化
                f[x][j+k]=max(f[x][j+k],f[x][j]+f[y][k]);
            }
        size[x]+=size[y];
    }
}
inline bool check(double mid){
    for(int i=1;i<=n;i++)
        v[i]=p[i]-s[i]*mid;
//記得根據mid更新每個人的價值
    dfs(0);//從根節點開始遍歷整個樹
    return f[0][m]>=0;//判定
}
int main(){
    m=read();n=read();
    m++;
//因為0號節點必選,所以相當於我們要選擇m+1個節點
    for(int i=1;i<=n;i++){
        s[i]=read();
        p[i]=read();
        r[i]=read();
        q[r[i]].push_back(i);
    }
//我直接vector存圖了,也可以用鏈式前向星
    double l=0,r=1e6,mid;
    while(l+eps<r){
        mid=(l+r)/2.0;
        if(check(mid))l=mid;
        else r=mid;
    }
    printf("%.3lf\n",l);
    return 0;
}

推薦1

推薦2

[JSOI2016]最佳團體