bzoj4753 最佳團體
題目描述
JSOI 信息學代表隊一共有 NN 名候選人,這些候選人從 11 到 NN 編號。方便起見,JYY 的編號是 00 號。每個候選人都由一位編號比他小的候選人R_iRi? 推薦。如果 R_i = 0Ri?=0?,則說明這個候選人是 JYY 自己看上的。
為了保證團隊的和諧,JYY 需要保證,如果招募了候選人 ii,那麽候選人 R_iRi? 也一定需要在團隊中。當然了,JYY 自己總是在團隊裏的。每一個候選人都有一個戰鬥值 P_iPi? ,也有一個招募費用 S_iSi? 。JYY 希望招募 KK 個候選人(JYY 自己不算),組成一個性價比最高的團隊。也就是,這 KK 個被 JYY 選擇的候選人的總戰鬥值與總招募費用的比值最大。
輸入輸出格式
輸入格式:
輸入一行包含兩個正整數 KK 和 NN 。
接下來 NN 行,其中第 ii 行包含三個整數 S_iSi? , P_iPi? , R_iRi? , 表示候選人 ii 的招募費用,戰鬥值和推薦人編號。
輸出格式:
輸出一行一個實數,表示最佳比值。答案保留三位小數。
這題涉及到了比值最大,最好用分數規劃來解決。
我們需要求出pi和ri的比值最大,不妨設Σpi/Σri>=x ,經過轉移Σpi>=Σri*x => Σpi-Σri*x>=0. 由此可見,我們可以二分出來一個x使這個值>=0.
然後我們可以用樹形DP來計算出最優值。
我們先DFS一遍得到每個樹上節點的DFS序(時間戳),令f[i][j]為DFS序為i的點,取j個的最優值。
如果當前點取,說明自己的子樹也可以取,所以f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+val[i]);
如果當前點不取,說明要取到下一顆樹,我們記錄size[i]代表以i為根的子樹的大小。根據DFS序的性質,我們知道下一顆和自己平行的子樹的DFS序為i+size.
所以轉移方程是:f[i+size[i]][j]=max(f[i+size[i]][j],f[i][j]);
// luogu-judger-enable-o2#include <iostream> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cstring> #include <stack> #define in(a) a=read() #define MAXN 200020 #define REP(i,k,n) for(int i=k;i<=n;i++) using namespace std; inline int read(){ int x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch==‘-‘) f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-‘0‘; return x*f; } int k,n; int INF=99999999; double p[2510],s[2510]; int total=0,to[2520],nxt[2510],head[2510]; int cnt=0,dfn[2510],ind[2510],size[2510]; double f[2510][2510],val[2510]; inline void adl(int a,int b){ total++; to[total]=b; nxt[total]=head[a]; head[a]=total; return ; } inline void DFS(int u){ dfn[u]=cnt; ind[cnt++]=u; size[u]=1; for(int e=head[u];e;e=nxt[e]){ DFS(to[e]); size[u]+=size[to[e]]; } return ; } inline double DP(double x){ //cout<<x<<endl; REP(i,1,n){ val[i]=p[ind[i]]-x*s[ind[i]]; //cout<<ind[i]<<" "<<val[i]<<endl; } REP(i,1,n+1) REP(j,0,k+1) f[i][j]=-INF; REP(i,0,n) REP(j,0,min(i,k+1)){ f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+val[i]); f[i+size[ind[i]]][j]=max(f[i+size[ind[i]]][j],f[i][j]); } /*REP(i,0,n){ REP(j,0,min(i,k+1)) cout<<f[i][j]<<" "; cout<<endl; }*/ return f[n+1][k+1]; } int main(){ in(k);in(n); int a; double maxn=-INF; REP(i,1,n){ scanf("%lf%lf%d",&s[i],&p[i],&a); adl(a,i); maxn=max(maxn,p[i]); } DFS(0); val[0]=0.0; double left=0.0,right=maxn; while(right-left>0.00001){ double mid=(left+right)/2.0; //cout<<left<<" "<<right<<" "<<mid<<endl; if(DP(mid)>=0.00001) left=mid; else right=mid; } printf("%.3lf",left); return 0; } /* 2 4 1 2 0 2 2 0 1 3 1 2 3 1 */
bzoj4753 最佳團體