码迷,mamicode.com
首页 > 其他好文 > 详细

树形动态规划专题

时间:2015-10-23 16:24:39      阅读:184      评论:0      收藏:0      [点我收藏+]

标签:

1.OJ1278战略游戏

  f[u][0]代表以u为根的子树,u不放时,最少放置节点数。

  f[u][1]代表以u为根的子树,u放时,最少放置节点数。

  f[u][0]=Σf[son][1]。

  f[u][1]=Σmin(f[son][1],f[son][0])。

  ans=min(f[root][0],f[root][1])。

技术分享
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=1500;
int n,root,tot,ans; 
int pre[maxn],now[maxn],son[maxn],f[maxn][2];
inline void read(int &x){
    char ch;
    x=0;
    while (ch=getchar(),ch== ||ch==\n);
    while (isdigit(ch)){
        x=x*10+ch-0;
        ch=getchar();
    }
}
inline void build(int u,int v){
    pre[++tot]=now[u];
    now[u]=tot;
    son[tot]=v;
}
void search(int u){
    f[u][1]=1;
    int p=now[u];
    while (p){
        int v=son[p];
        search(v);
        f[u][1]+=min(f[v][1],f[v][0]);
        f[u][0]+=f[v][1];
        p=pre[p];
    }
}
void init(){
    read(n);
    root=(n-1)*n/2;
    int u,k,v;
    for (int i=1;i<=n;++i){
        read(u),read(k);
        for (int j=1;j<=k;++j)
            read(v),root-=v,build(u,v);
    }
}
void work(){
    search(root);
    ans=min(f[root][0],f[root][1]);
    printf("%d\n",ans);
}
int main(){
    init();
    work();
    return 0;
}
my code

2.OJ1264[Ural1018 ]二叉苹果树

  f[u][i]代表以u为根的子树,保留i条边,最多能留下的苹果数。

  f[u][0]=0。

  枚举son,逆枚举i,f[u][i]=max(f[u][i],f[son][j]+f[u][i-j-1])。

  ans=f[root][q]。

技术分享
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=115;
int n,q,tot,now[maxn],pre[maxn<<1],son[maxn<<1],val[maxn<<1];
void connect(int u,int v,int w){pre[++tot]=now[u];now[u]=tot;son[tot]=v;val[tot]=w;}
void init(){
    scanf("%d%d",&n,&q);
    for (int u,v,w,i=1;i<=n-1;++i){
        scanf("%d%d%d",&u,&v,&w);
        connect(u,v,w);connect(v,u,w);
    }
}
int f[maxn][maxn];
void tree_dp(int u,int fa){
    f[u][0]=0;
    for (int p=now[u];p;p=pre[p]){
        if (son[p]==fa) continue;
        tree_dp(son[p],u);
        for (int i=q;i>=1;--i)
            for (int j=0;j<=i-1;++j)
                f[u][i]=max(f[u][i],f[son[p]][j]+f[u][i-j-1]+val[p]);
    }
}
void work(){
    memset(f,200,sizeof(f));
    tree_dp(1,0);
    printf("%d\n",f[1][q]);
}
int main(){
    init();
    work();
    return 0;
}
my code

3.OJ1277有线电视网

  f[u][i]代表以u为根的子树,满足子树中i个叶子节点,所获得最大的收益。

  f[u][0]=0。

  如果u是叶子节点,f[u][1]=v[u]。

  否则,枚举son,逆枚举i,f[u][i]=max(f[u][i],f[son][j]-val[p]+f[u][i-j])。

  注意j只需枚举到以son为根的子树的叶子数目即可,i=∑leaf_num(当前已枚举到的son)。

  还可以将son按leaf_num排序从小到大排序,会快一些,但我并不会算复杂度。

  ans=最大的i,满足f[root][i]>=0。

技术分享
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=3e3+15;
typedef pair<int,int> PII;
int n,m,v[maxn];vector<PII> g[maxn];
void init(){
    scanf("%d%d",&n,&m);
    for (int tot,i=1;i<=n-m;++i){
        scanf("%d",&tot);
        for (int a,c,j=1;j<=tot;++j){
            scanf("%d%d",&a,&c);
            g[i].push_back(make_pair(a,c));
        }
    }
    for (int i=n-m+1;i<=n;++i) scanf("%d",&v[i]);
}
int f[maxn][maxn];
int tree_dp(int u){
    f[u][0]=0;
    if (u>=n-m+1){f[u][1]=v[u];return 1;}
    int s,sum=0;
    for (unsigned int i=0;i<g[u].size();++i){
        sum+=(s=tree_dp(g[u][i].first));
        for (int j=sum;j>=1;--j)
            for (int k=1;k<=min(j,s);++k)
                f[u][j]=max(f[u][j],f[u][j-k]+f[g[u][i].first][k]-g[u][i].second);
    }
    return sum;
}
void work(){
    memset(f,200,sizeof(f));
    tree_dp(1);
    for (int i=m;i>=0;--i)
        if (f[1][i]>=0){
            printf("%d\n",i);
            break;
        }
}
int main(){
    init();
    work();
    return 0;
}
my code

4.OJ1274“访问”艺术馆

  f[u][i]代表以u为根的子树,花费了i的时间,所获得最大收益。

  如果i是叶子节点,背包即可。

  否则,枚举son,逆枚举i,f[u][i]=max(f[u][i],f[son][j]+f[u][i-j-2*val[p]])。

技术分享
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
const int maxn=615,maxt=615;
vector<PII> node[maxn];
int tim,cnt,len[maxn],son[maxn][2];
void init(int u){
    int t,x;scanf("%d%d",&t,&x);len[u]=t;
    if (!x){init(son[u][0]=++cnt);init(son[u][1]=++cnt);}
    else{
        for (int w,c,i=1;i<=x;++i){
            scanf("%d%d",&w,&c);
            node[u].push_back(make_pair(w,c));
        }
    }
}
int f[maxn][maxt];
void tree_dp(int u){
    if (node[u].empty()){
        tree_dp(son[u][0]);
        tree_dp(son[u][1]);
        for (int i=1;i<=tim;++i)
            for (int j=0;j<=i;++j){
                int t=0;
                if (j>=2*len[son[u][0]]) t+=f[son[u][0]][j-2*len[son[u][0]]];
                if (i-j>=2*len[son[u][1]]) t+=f[son[u][1]][i-j-2*len[son[u][1]]];
                f[u][i]=max(f[u][i],t);
            }
    }
    else{
        for (unsigned int i=0;i<node[u].size();++i)
            for (int j=tim;j>=node[u][i].second;--j)
                f[u][j]=max(f[u][j],f[u][j-node[u][i].second]+node[u][i].first);
    }
}
int main(){
    scanf("%d",&tim);init(cnt=1);
    tim-=len[1]*2;tree_dp(1);
    printf("%d",f[1][tim-1]);
    return 0;
}
my code

5.OJ1216[Ioi2005]River

  dis[u][j]代表u向上走j步的距离。

  f[u][i][j]代表以u为根的子树建了i个伐木场(不包括u节点的),到跟的路径上第一个伐木场是u的第j个祖先。

  枚举每个儿子,逆枚举i,

    f[u][i][j]=min(f[u][i][j],f[son][k][j+1]+w[son]*dis[son][j+1]+f[u][i-k][j])。

    if (i>k) f[u][i][j]=min(f[u][i][j],f[son][k][0]+f[u][i-k-1][j])。

  ans=f[root][m][0]。

技术分享
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=115,maxk=65;
vector<int> g[maxn];
int n,m,w[maxn],fa[maxn],len[maxn];
void init(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i){
        scanf("%d%d%d",&w[i],&fa[i],&len[i]);
        g[fa[i]].push_back(i);
    }
}
typedef unsigned int uint;
int dis[maxn],anc[maxn][maxn];
void prepare(int u,int dep){
    anc[u][1]=fa[u];dis[u]=dis[fa[u]]+len[u];
    for (int i=2;i<=dep;++i) anc[u][i]=anc[fa[u]][i-1];
    for (uint i=0;i<g[u].size();++i) prepare(g[u][i],dep+1);
}
const int inf=1e9;
int f[maxn][maxk][maxn],t[maxn][maxk][maxn];
void tree_dp(int u,int dep){
    for (uint i=0;i<g[u].size();++i){
        int v=g[u][i];
        tree_dp(v,dep+1);
        for (int l=0;l<=dep;++l)
            for (int j=0;j<=m;++j){
                f[u][j][l]=inf;
                for (int k=0;k<=j;++k){
                    if (j-k>0) f[u][j][l]=min(f[u][j][l],t[u][j-k-1][l]+f[v][k][0]);
                    f[u][j][l]=min(f[u][j][l],t[u][j-k][l]+f[v][k][l+1]+w[v]*(dis[v]-dis[anc[v][l+1]]));
                }
            }
        memcpy(t[u],f[u],sizeof(t[u]));
    }
}
void work(){
    prepare(0,0);
    tree_dp(0,0);
    printf("%d\n",f[0][m][0]);
}
int main(){
    init();
    work();
    return 0;
}
my code

6.OJ2412[Ahoi99]圣诞树游戏

  f[i]代表将i点亮所需最小电流。

  对于节点u,将儿子按f[son]从大到小排序,f[u]=max(f[u],f[son]+当前已枚举儿子数(不包括当前节点))。

  ans=f[root]。

技术分享
var
  x,n,m,j,k,i:longint;
  f:array[0..100] of longint;
  son:array[0..100,0..100] of longint;

function max(p,q:longint):longint;
begin
  if p>q then exit(p);
  exit(q);
end;

procedure sort(x,q:Longint);
var
  t,i,j:longint;
begin
  for i:=1 to q-1 do
    for j:=i+1 to q do
      if f[son[x,i]]<f[son[x,j]] then
      begin
        t:=f[son[x,i]];
        f[son[x,i]]:=f[son[x,j]];
        f[son[x,j]]:=t;
      end;
end;

procedure dfs(x:longint);
var
  i:longint;
begin
  for i:=1 to son[x,0] do
    dfs(son[x,i]);
  sort(x,son[x,0]);
  for i:=1 to son[x,0] do
    f[x]:=max(f[x],f[son[x,i]]+i-1);
end;

begin
  read(n);
  for i:=1 to n do
  begin
    read(x);
    inc(son[x,0]);
    son[x,son[x,0]]:=i;
  end;
  readln;
  for i:=1 to n do
    f[i]:=son[i,0]+1;
  dfs(1);
  writeln(f[1]);
end.
my code

7.OJ1217[baltic2003]gems

  f[u][i]代表以u为根的子树,u节点数值为i,的最小数值和。

  枚举son,枚举i,f[u][i]=min(f[son][j],j!=i)+i。

  ans=min(f[root][i])。

技术分享
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10015,maxm=5;
int n,tot,now[maxn],pre[maxn<<1],son[maxn<<1];
void connect(int u,int v){pre[++tot]=now[u];now[u]=tot;son[tot]=v;}
void init(){
    scanf("%d",&n);
    for (int u,v,i=1;i<=n-1;++i){
        scanf("%d%d",&u,&v);
        connect(u,v);connect(v,u);
    }
}
int f[maxn][maxm];
void tree_dp(int u,int fa){
    static int g[maxm];
    for (int i=1;i<maxm;++i) f[u][i]=i;
    for (int p=now[u];p;p=pre[p]){
        if (son[p]==fa) continue;
        tree_dp(son[p],u);
        memset(g,64,sizeof(g));
        for (int i=1;i<maxm;++i){
            for (int j=1;j<maxm;++j)
                if (i!=j) g[i]=min(g[i],f[son[p]][j]);
            f[u][i]+=g[i];
        }
    }
}
void work(){
    tree_dp(1,0);int res=1e9;
    for (int i=1;i<maxm;++i) res=min(res,f[1][i]);
    printf("%d\n",res);
}
int main(){
    init();
    work();
    return 0;
}
my code

8.OJ1326[Noi2003]逃学的小孩

  对于树上三点a,b,c,求最大的dis[a][b]+dis[b][c],满足dis[a][b]<=dis[a][c]。

  可以脑补dis[a][c]即为直径,a,c即为两端点,然后以a,c为源求disa,disc。

  ans=max(dis[a][c]+min(dis[a][b],dis[c][b]))。

技术分享
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=2e5+15;
typedef long long int64;
int n,m,tot,now[maxn],pre[maxn<<1],son[maxn<<1],val[maxn<<1];
void connect(int u,int v,int w){pre[++tot]=now[u];now[u]=tot;son[tot]=v;val[tot]=w;}
void init(){
    scanf("%d%d",&n,&m);
    for (int u,v,w,i=1;i<=m;++i){
        scanf("%d%d%d",&u,&v,&w);
        connect(u,v,w);connect(v,u,w);
    }
}
int fa[maxn],q[maxn];int64 dis[maxn];
void bfs(int s){
    memset(dis,0,sizeof(dis));
    int head=0,tail=1;q[1]=s;fa[s]=0;
    while (head!=tail){
        int u=q[++head];
        for (int p=now[u];p;p=pre[p])
            if (son[p]!=fa[u]){
                fa[son[p]]=u;
                q[++tail]=son[p];
                dis[son[p]]=dis[u]+val[p];
            }
    }
}
pair<int,int> node;
void work(){
    bfs(1);node.first=1;
    for (int i=2;i<=n;++i) if (dis[i]>dis[node.first]) node.first=i;
    bfs(node.first);node.second=1;
    for (int i=2;i<=n;++i) if (dis[i]>dis[node.second]) node.second=i;
    int64 d=dis[node.second];
    static int64 dis1[maxn],dis2[maxn];
    bfs(node.first);memcpy(dis1,dis,sizeof(dis));
    bfs(node.second);memcpy(dis2,dis,sizeof(dis));
    int64 res=0;
    for (int i=1;i<=n;++i) res=max(res,min(dis1[i],dis2[i]));
    printf("%I64d\n",res+d);
}
int main(){
    init();
    work();
    return 0;
}
my code

9.OJ1218[balkan2002]Tribe council

  UNsolved。

10.OJ1269OI队的回家路 Pku1947 Rebuilding Roads

  f[u][i]代表以u为根的子树保留i个节点最少要切几条边。

  f[u][1]=0。

  枚举son,逆枚举i,另t=做当前儿子之前的f[u][i]

    f[u][i]=min(t+1,f[u][j]+f[son][i-j])。

  ans=min(f[root][p],min(f[u][p]+1))。

技术分享
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=215;
int n,m,tot,root,now[maxn],pre[maxn],son[maxn];
void connect(int u,int v){pre[++tot]=now[u];now[u]=tot;son[tot]=v;}
void init(){
    scanf("%d%d",&n,&m);root=(n+1)*n/2;
    for (int u,v,i=1;i<=n-1;++i){
        scanf("%d%d",&u,&v);root-=v;
        connect(u,v);
    }
}
int g[maxn][maxn],f[maxn][maxn];
void tree_dp(int u){
    f[u][1]=0;
    for (int p=now[u];p;p=pre[p]){
        tree_dp(son[p]);
        for (int i=m;i>=1;--i)
            for (int j=0;j<=i-1;++j)
                if (j) f[u][i]=min(f[u][i],f[u][j]+f[son[p]][i-j]);
                else f[u][i]=f[u][i]+1;
    }
}
void work(){
    memset(g,63,sizeof(g));
    memset(f,63,sizeof(f));
    tree_dp(root);
    int res=f[root][m];
    for (int i=1;i<=n;++i) res=min(res,f[i][m]+1);
    printf("%d\n",res);
}
int main(){
    init();
    work();
    return 0;
}
my code

11.OJ2577[Nwerc2009]Moving to Nuremberg

  预处理以u为根的子树中的标记次数sum[u],和标记点(多次要计算)到root的距离和dis[root]。

  dis[son]=dis[u]-sum[son]*val[p]+(sum[root]-sum[son])*val[p]。

  ans=min(dis[u])。

技术分享
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=5e4+15;
typedef long long int64;
int n,m,tot,tim[maxn],now[maxn],pre[maxn<<1],son[maxn<<1],val[maxn<<1];
void connect(int u,int v,int w){pre[++tot]=now[u];now[u]=tot;son[tot]=v;val[tot]=w;}
int head,tail,q[maxn],fa[maxn];int64 tottim,f[maxn],v[maxn],sdis[maxn],stim[maxn];
void get_sum_dist(){
    q[1]=1;fa[1]=0;head=0;tail=1;
    memset(sdis,0,sizeof(int64)*(n+1));
    memset(stim,0,sizeof(int64)*(n+1));
    while (head!=tail){
        int u=q[++head];
        for (int p=now[u];p;p=pre[p])
            if (son[p]!=fa[u]){
                q[++tail]=son[p];
                v[son[p]]=val[p];
                fa[son[p]]=u;
            }
    }
    for (int i=tail;i>=1;--i){
        int u=q[i];
        stim[u]=tim[u];
        for (int p=now[u];p;p=pre[p]){
            stim[u]+=stim[son[p]];
            sdis[u]+=stim[son[p]]*val[p]+sdis[son[p]];
        }
    }
}
void get_ans(){
    f[1]=sdis[1];
    for (int i=2;i<=tail;++i){
        int u=q[i];
        f[u]=f[fa[u]]-stim[u]*v[u]+(tottim-stim[u])*v[u];
    }
    int64 res=f[1];
    for (int i=2;i<=n;++i) res=min(res,f[i]);
    printf("%I64d\n",2*res);
    for (int i=1;i<=n;++i) if (f[i]==res) printf("%d ",i);
    putchar(\n);
}
void solve(){
    scanf("%d",&n);tot=tottim=0;
    memset(now,0,sizeof(int)*(n+1));
    for (int u,v,w,i=1;i<=n-1;++i){
        scanf("%d%d%d",&u,&v,&w);
        connect(u,v,w);connect(v,u,w);
    }
    scanf("%d",&m);
    memset(tim,0,sizeof(int)*(n+1));
    for (int x,f,i=1;i<=m;++i){
        scanf("%d%d",&x,&f);
        tim[x]=f;tottim+=f;
    }
    get_sum_dist();
    get_ans();
}
int main(){
    int cases;scanf("%d",&cases);
    for (int i=1;i<=cases;++i) solve();
    return 0;
}
my code

12.OJ1213[zjoi2007]时态同步

  贪心,求出u到叶子的最远距离fmx[u],res+=Σ(fmx[u]-fmx[son[p]]+val[p])。

  ans=res。

技术分享
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=500015;
int n,s,tot,now[maxn],pre[maxn<<1],son[maxn<<1],val[maxn<<1];
void connect(int u,int v,int w){pre[++tot]=now[u];now[u]=tot;son[tot]=v;val[tot]=w;} 
void init(){
    scanf("%d%d",&n,&s);
    for (int u,v,w,i=1;i<=n-1;++i){
        scanf("%d%d%d",&u,&v,&w);
        connect(u,v,w);connect(v,u,w);
    }
}
long long ans;
int maxdis[maxn];//std ????long long ??RZ 
void greedy(int u,int f){
    for (int p=now[u];p;p=pre[p])
        if (son[p]!=f){
            greedy(son[p],u);
            maxdis[u]=max(maxdis[u],maxdis[son[p]]+val[p]);
        }
    for (int p=now[u];p;p=pre[p])
        if (son[p]!=f)
            ans+=maxdis[u]-(maxdis[son[p]]+val[p]);
}
void work(){
    greedy(s,0);
    printf("%I64d\n",ans);
}
int main(){
    init();
    work();
    return 0;
}
my code

13.OJ1280[Noi2008]道路设计

  f[u][i][j],j=0,1,2,代表以u为根的子树,u向下连了j条边,答案为i的方案数。

  f[u][i][0]=Π(f[son][i-1][0]+f[son][i-1][1]+f[son][i-1][2])。

  f[u][i][1]=Σ{(f[son][i][0]+f[son][i][1])Π(f[son‘][i-1][0]+f[son‘][i-1][1]+f[son‘][i-1][2])}。

  f[u][i][2]=ΣΣ{(f[son][i][0]+f[son][i][1]+f[son‘][i][0]+f[son‘][i][1])Π(f[son‘‘][i-1][0]+f[son‘‘][i-1][1]+f[son‘‘][i-1][2])}。

  令f0=f[son][i-1][0]+f[son][i-1][1]+f[son][i-1][2],f1=f[son][i][0]+f[son][i][1]。

  则f[u][i][0]=Πf0,f[u][i][1]=Σ(f1Πf0‘),f[u][i][2]=ΣΣ(f1f1‘Πf0‘‘)。

  枚举每个son,考虑每次带来的新贡献,则有,

    f[u][i][0]=f[u][i][0]‘*f0。

    f[u][i][1]=f1*f[u][i][0]‘+f0*f[u][i][1]‘。

    f[u][i][2]=f1*f[u][i][1]‘+f0*f[u][i][2]‘。

  ans=最大的i,满足max(f[root][i][0],f[root][i][1],f[root][i][2])>0。

技术分享
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e5+15;
typedef long long int64;
int64 n,m,q,tot,now[maxn],pre[maxn<<1],son[maxn<<1];
void connect(int u,int v){pre[++tot]=now[u];now[u]=tot;son[tot]=v;}
void init(){
    scanf("%lld%lld%lld",&n,&m,&q);
    if (m!=n-1){printf("-1\n-1\n");exit(0);}
    for (int u,v,i=1;i<=n-1;++i){
        scanf("%d%d",&u,&v);
        connect(u,v);connect(v,u);
    }
}
const int max_ans=15;
int64 f[maxn][max_ans][3];//代表以i为根的子树,当前答案为j,且根向下连了k条边的方案数
/*
    f[i][j][0]=PI(f[son][j-1][0]+f[son][j-1][1]+f[son][j-1][2]);
    f[i][j][1]=SIGMA((f[son][j][0]+f[son][j][1])*PI(f[son‘][j-1][0]+f[son‘][j-1][1]+f[son‘][j-1][2]));
    f[i][j][2]=SIGMA((f[son][j][0]+f[son][j][1])*(f[son‘][j][0]+f[son‘][j][1])*PI(f[son‘‘][j-1][0]+f[son‘‘][j-1][1]+f[son‘‘][j-1][2]));
    令f1=f[son][j-1][0]+f[son][j-1][1]+f[son][j-1][2],f2=f[son][j][0]+f[son][j][1]
    f[i][j][0]=PI(f1);
    f[i][j][1]=SIGMA(f2*PI(f1));
    f[i][j][2]=SIGMA(f2*f2*PI(f1));
    for each son
        f[i][j][2]=f[i][j][2]*f1+f[i][j][1]*f2;
        f[i][j][1]=f[i][j][1]*f1+f[i][j][0]*f2;
        f[i][j][0]*=f1;
*/
int64 get(int64 x){return !(x%q)&&x?q:x%q;}
void tree_dp(int u,int fa){
    for (int i=0;i<max_ans;++i) f[u][i][0]=1;
    for (int p=now[u];p;p=pre[p]){
        if (son[p]==fa) continue;
        tree_dp(son[p],u);
        for (int i=0;i<max_ans;++i){
            int64 f1=i?f[son[p]][i-1][0]+f[son[p]][i-1][1]+f[son[p]][i-1][2]:0;
            int64 f2=f[son[p]][i][0]+f[son[p]][i][1];
            f[u][i][2]=get(f[u][i][2]*f1+f[u][i][1]*f2);
            f[u][i][1]=get(f[u][i][1]*f1+f[u][i][0]*f2);
            f[u][i][0]=get(f[u][i][0]*f1);
        }
    }
}
void work(){
    tree_dp(1,0);
    for (int i=0;i<max_ans;++i){
        int64 t=f[1][i][0]+f[1][i][1]+f[1][i][2];
        if (t>0){printf("%d\n%d\n",i,(int)(t%q));exit(0);}
    }
    printf("-1\n-1\n");
}
int main(){
    init();
    work();
    return 0;
}
my code

14.OJ3155[CQOI2009]叶子的染色

  f[u][i],代表以u为根的子树颜色为i(当i=2时所有颜色均满足)的节点全部满足的最小染色数。

  若u为叶子,f[u][c[u]]=1,f[u][c[u]^1]=0,f[u][2]=1。

  否则,枚举son,设t0,t1,t2

    t0=∑f[son][0]。

    t1=∑f[son][1]。

    t2=∑f[son][2]。

  f[u][2]=min(t0+1,t1+1,t2)。

  f[u][0]=min(t0,t1+1,f[u][2])。

  f[u][1]=min(t1,t0+1,f[u][2])。

技术分享
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxm=1e4+15,maxn=6e3+15;
int m,n,tot,c[maxm],now[maxm],pre[maxm<<1],son[maxm<<1];
void connect(int u,int v){pre[++tot]=now[u];now[u]=tot;son[tot]=v;}
void init(){
    scanf("%d%d",&m,&n);
    for (int i=1;i<=n;++i) scanf("%d",&c[i]);
    for (int u,v,i=1;i<=m-1;++i){
        scanf("%d%d",&u,&v);
        connect(u,v);connect(v,u);
    }
}
int f[maxm][3];//0 只满足0 1 只满足1 2 全满足 的 最小花费 
void tree_dp(int u,int fa){
    if (u<=n){
        f[u][2]=1;
        f[u][c[u]]=1;
        f[u][c[u]^1]=0;
        return;
    }
    int sum0=0,sum1=0,sum2=0;
    for (int p=now[u];p;p=pre[p]){
        if (son[p]==fa) continue;
        tree_dp(son[p],u);
        sum0+=f[son[p]][0];
        sum1+=f[son[p]][1];
        sum2+=f[son[p]][2];
    }
    f[u][2]=min(sum2,min(sum0,sum1)+1);
    f[u][0]=min(f[u][2],min(sum0,sum1+1));
    f[u][1]=min(f[u][2],min(sum0+1,sum1));
}
void work(){
    tree_dp(n+1,-1);
    printf("%d\n",f[n+1][2]);
}
int main(){
    init();
    work();
    return 0;
}
my code

待更新。

树形动态规划专题

标签:

原文地址:http://www.cnblogs.com/iamCYY/p/4904572.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!