qbxt Day4
1.樹形dp
例題1
樹上最長鏈
其實有兩種方法,但為了簡便,就只學了最通用的dp算法
我們考慮設dp[i][0/1]表示以i為根的最長路和次長路,然后拼接就行了
第二維0表示最長路,1表示次長路
if dp[i][0]<dp[son][0] then dp[i][1]=dp[i][0],dp[i][0]=dp[son][0]
if dp[i][0]>dp[son][0] && dp[i][1]<dp[son][0] then dp[i][1]=dp[son][0]
就可以了
例題2
codevs1378
有n節課可以選,每節課有至多一個前置課程,和這節課的學分,問如果只能選m節課,最多有多少學分。
這是一道類似于樹形背包的dp
dp么,需要記錄所有可行狀態
考慮設dp[i][j]在以i為根的子樹中選除j個課程的最大價值
每次新增一個兒子,就相當于新增加一個多重背包中的物品。此處暴力枚舉就可以了,時間復雜度O(n^3)
#include<cstdio> #include<algorithm> #include<iostream> using std::max; using std::min; const int maxn=310; struct node {int point;int nxt; }; node line[maxn<<1]; int s[maxn]; int head[maxn],tail; int ind[maxn]; int f[maxn][maxn]; int siz[maxn]; int n,m; void add(int a,int b) {line[++tail].point=b;line[tail].nxt=head[a];head[a]=tail; } void dfs(int now) {siz[now]=1;f[now][1]=s[now];for(int i=head[now];i;i=line[i].nxt){dfs(line[i].point);for(int j=siz[now]+siz[line[i].point];j>=2;j--)for(int k=1;k<=min(siz[line[i].point],j-1);k++)f[now][j]=max(f[now][j],f[now][j-k]+f[line[i].point][k]);siz[now]+=siz[line[i].point]; } } int main() {scanf("%d%d",&n,&m);int dat;for(int i=1;i<=n;i++){scanf("%d%d",&dat,&s[i]);add(dat,i);}dfs(0);printf("%d",f[0][m+1]); }例題3
hdu4679
給定一顆樹,每條邊有一個權值w,問切掉哪條邊之后,分成的兩顆樹的較大的直徑*切掉邊的權值最小?如果存在多條邊使得結果相同,輸出邊id最小的。
考慮枚舉切哪一條邊,若不在整棵樹的直徑上,處理就很好處理
然后我們考慮如果在直徑上呢? 考慮從直徑的左右兩端點分別為根做一次dp,然后每次切邊。將一棵樹分成兩棵樹,這樣就可以在o(1)時間內算出分離后兩顆子樹的直徑了
預處理多么重要呀~~
例題4
codeforces219d
給一棵樹,每條邊有方向,改變一條邊方向的代價是1.
對于一個點,如果選它為根,那么需要把方向不對的邊改變方向(都變成深度小的點指向深度大的點)。
問選一個點為根的最小代價。和選哪些點的代價是這個數字。
其實這道題,如果看過平衡樹中的rotate操作,其實不難。因為一次換根的時間是O(1)的
然后先選出一個點來,算出其所需次數,然后將其他點作為根試一次,然后就是O(n)的時間復雜度
例題4
bzoj1040
給一個環套樹森林,求最大權獨立集。(就是相鄰的點不能同時選)
首先考慮dp一開始考慮做出決策的方法——枚舉
首先每個點只有選與不選,是可以接受的。
然后我們考慮枚舉環上的點,斷環為鏈。再使用樹形dp
但是我們在枚舉的時候要枚舉相鄰的的兩個點。防止不合法的情況
然后我還沒有寫出來QAQ
轉載于:https://www.cnblogs.com/Lance1ot/p/9333418.html
總結
- 上一篇: shell脚本中执行mysql 语句,去
- 下一篇: P3572 [POI2014]PTA-L