1. 程式人生 > >樹形dp瞎講+樹形dp基礎題題解

樹形dp瞎講+樹形dp基礎題題解

---恢復內容開始---

沒錯

咕了這麼久(沒有青青姐久

我又開始寫部落格了( ´▽`)

想了很久些什麼(才沒有想過呢

雖然被鄙視基礎不好但還是走上了樹形dp的不歸路

那麼

就來寫寫樹形dp吧(dtx daolao不要打我

樹形dp是什麼呢?

一言概之,dfs中的動態規劃

emmmmm

因為沒什麼固定格式

就推轉移方程(所以我一開始根本找不到講樹形dp的blog

然後發現了良心部落格一枚

放一下連結

基本的dp方程

選擇節點類

dp[i][0]=dp[j][1]
dp[i][1]=max/min(dp[j][0],dp[j][1]){dp[i][0]=dp[j][1]dp[i][1]=max/min(dp[j][0],dp[j][1])

樹形揹包類

dp[v][k]=dp[u][k]+val
[v][k−1])">dp[u][k]=max(dp[u][k],dp[v][k1])

雖然關於樹形dp的部落格基本上都是題

但是看懂了基本dp方程,理解了實質大概就不會特別難(我這句話真嚴謹

然後

題還是有很多的

就不放題面了

放連結好了

要是放一堆圖片有假裝寫了很多的嫌疑

傳送門——>luogu P1352 沒有上司的舞會

核心的程式碼就是上邊放的狀態轉移方程

#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 6010

int
fa[maxn],dp[maxn][2]; int root,n; int v[maxn]; int nxt[maxn],head[maxn]; void treedp(int x){ for(int i = head[x];i;i = nxt[i]){ treedp(i); dp[x][0] += max(dp[i][1],dp[i][0]);//dp[x][0]代表上司x不來時的最大快樂指數 dp[x][1] += dp[i][0];//dp[x][1]代表上司來的 } } int main() { scanf("%d",&n); for(int i = 1; i <= n; i++) scanf("%d",&dp[i][1]); for(int i = 1; i < n; i++) { int x,y; scanf("%d%d",&x,&y); v[x] ++;//記錄一下有沒有上司 nxt[x] = head[y]; head[y] = x;//對,像鏈式前向星的東西(我還理解了好久【劃掉 } for(int i = 1; i <= n; i++) if(!v[i]) {//沒有上司的就是最大上司啦 root = i; break; } treedp(root);//從樹根開始dp printf("%d",max(dp[root][0],dp[root][1]));//取最高上司來和不來的最大快樂指數 return 0; }

emmmmm

還是蠻好懂的吧

然後上下一道

傳送門 ———>luogu P1122 最大子樹和

這裡有一點點的變化

就是剪掉了之後是加0

#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 16010

int dp[maxn];
int cnt,head[maxn * 2];
int f[maxn];//記錄當前子節點到之下的花的最大值
int ans = -2147483647;//因為可能全是負的所以ans搞到最小

struct EDGE{
  int nxt,to;
}edge[maxn * 2];

void add(int x,int y){
  edge[++cnt].to = y;
  edge[cnt].nxt = head[x];
  head[x] = cnt;
}//正常的加邊看上去真舒服啊

void treedp(int u,int fa){
  f[u] = dp[u];//給f[u]賦值為它本身的大小
  for(int i = head[u];i;i = edge[i].nxt){
    int v = edge[i].to;//v是u的兒子
    if(v != fa){//唔防止雙向加邊跑回去
      treedp(v,u);
      f[u] += max(0,f[v]);
    }
  }
  ans = max(ans,f[u]);//反正所有的花都在一根上不需要加和什麼的(我在說什麼廢話
}

int main(){
  int n;
  scanf("%d",&n);
  for(int i = 1;i <= n;i++)
    scanf("%d",&dp[i]);
  for(int i = 1;i < n;i++){
    int x,y;
    scanf("%d%d",&x,&y);
    add(x,y);
    add(y,x);
  }
  treedp(1,0);//將1的父節點定義為0
  printf("%d",ans);
  return 0;
}

唔嗷【好睏

(明天的聯歡是我寫部落格的動力【不小心暴露了什麼

下一道來

傳送門———>luogu P2016 戰略遊戲

(嚇死我了插連結的時候突然閃退了(╥﹏╥)還好有自動儲存這種可愛的東西

emmmm

這道題似乎又回到了原點?

其實跟沒有上司的舞會很像

就是上司不來,下屬必須來

上司來,下屬來不來都行

(有點像開會?總得來一個負責人,當然都希望來的越少越好了【因為懶

#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 1510

struct EDGE{
  int nxt,to;
}edge[maxn];

int cnt,head[maxn];
int v[maxn];
int dp[maxn][2];

void add(int x,int y){
  edge[++cnt].to = y;
  edge[cnt].nxt = head[x];
  head[x] = cnt;
}

void treedp(int x){
  dp[x][1] = 1;//放的地方都是一
  for(int i = head[x];i;i = edge[i].nxt){
    int u = edge[i].to;
    treedp(u);
    dp[x][0] += dp[u][1];//當前點不放,下一個點一定要放
    dp[x][1] += min(dp[u][1],dp[u][0]);當前放了下一個點不一定放不放
  }
}

int main(){
  int n;
  scanf("%d",&n);
  for(int i = 0;i < n;i++){
    int r,k;
    scanf("%d%d",&r,&k);
    for(int j = 0;j < k;j++){
      int p;
      scanf("%d",&p);
      v[p]++;
      add(r,p);
    }
  }
  int root;
  for(int i = 0;i < n;i++)
    if(!v[i]){
      root = i;
      break;
    }//唔又是找根
  treedp(root);
  printf("%d",min(dp[root][0],dp[root][1]));
  return 0;
}

啊友情提示

a掉這道題之後可以順道a掉luogu UVA1292 Strategic game

差別就是這道題要輸入多組資料而已

/*

emmmmm

去寫選課

寫完就發

 */

好的我鴿了

告辭【快速溜