樹上dp入門
阿新 • • 發佈:2019-02-09
樹形dp就是把操作放在樹上。
1. P1352 沒有上司的舞會 ——樹的最大獨立集
題意:上司如果參加酒會,下屬就不去了。很容易想到上司和員工之間的關係就是父節點與子節點的關係,用boss[i]來儲存i的父節點編號。因為編號是1-n的嘛,我們就初始化boss[]為0,然後遍歷知道根節點,顯然,沒有父節點的那個為根節點(boss[i]==0).
狀態定義:dp[i][0]為i不參加時i和他的下屬製造的最大歡樂度;
dp[i][0]為i參加時i和他的下屬製造的最大歡樂度。
#include<iostream> #include<cstdio> #include<cmath> #include<vector> using namespace std; const int maxn=6e3+10; int a[maxn]; int n,boss[maxn],dp[maxn][2]; vector<int> E[maxn]; void dfs(int x){ dp[x][0]=0; dp[x][1]=a[x]; for(int i=0;i<E[x].size();i++){ int tmp=E[x][i]; dfs(tmp); dp[x][0]+=max(dp[tmp][0],dp[tmp][1]); //沒取父節點的值,子節點分為取或不取兩種情況,取其中值較大的那種情況 dp[x][1]+=dp[tmp][0]; //既然去了父節點的值,子節點的值不能取了 } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int L,k; while(~scanf("%d%d",&L,&k)){ if(L==0&&k==0) break; boss[L]=k; E[k].push_back(L); } for(int i=1;i<=n;i++){ if(boss[i]==0){ dfs(i); printf("%d\n",max(dp[i][0],dp[i][1])); break; } } return 0; }
因為要排序,演算法的時間複雜度為O(nlogn) 。
#include<iostream> #include<vector> #include<algorithm> #include<cmath> using namespace std; const int maxn = 100000 + 5; int n, t; vector<int> sons[maxn]; int dp(int u) //dp(u)表示讓u給上級發信最少需要多少個工人。工人也就是葉子節點 { if (sons[u].empty()) return 1; vector<int> d; int k = sons[u].size(); for (int i = 0; i < k; i++) d.push_back(dp(sons[u][i])); sort(d.begin(), d.end()); //int c = (k*t - 1) / 100 + 1; //上取整語句,等價於下邊那一句 int c=ceil(k*t/100.0); //cout<<"c="<<c<<endl; int ans = 0; for (int i = 0; i < c; i++){ ans += d[i]; //cout<<"d["<<i<<"]="<<d[i]<<",ans="<<ans<<endl; } return ans; } int main() { int temp; while (cin >> n >> t && (n || t)) { for (int i = 0; i <= n; i++) sons[i].clear(); for (int i = 1; i <= n; i++) { cin >> temp; sons[temp].push_back(i); } int ans=dp(0); cout << ans << endl; } return 0; }
再次寫的程式碼,
#include<iostream> #include<cmath> #include<vector> #include<algorithm> using namespace std; const int maxn=1e5+10; vector<int> sons[maxn]; int n,t; int dp(int x){ if(sons[x].empty()) return 1; int ans=0; int k=sons[x].size(); vector<int> w; for(int i=0;i<k;i++){ //cout<<"子節點:"<<sons[x][i]<<",dp(sons[x][i])="<<dp(sons[x][i])<<endl; w.push_back(dp(sons[x][i])); } /* for(int i=0;i<k;i++){ cout<<"w[i]="<<w[i]<<endl; }*/ sort(w.begin(),w.end()); int c=ceil(k*t/100.0); //cout<<"c="<<c<<endl; for(int i=0;i<c;i++) ans+=w[i]; return ans; } int main(){ while(cin>>n>>t){ if(n==0&&t==0) break; for(int i=0;i<=n;i++) sons[i].clear(); int tmp; for(int i=1;i<=n;i++){ cin>>tmp; sons[tmp].push_back(i); } cout<<dp(0)<<endl;; } }