• BZOJ 3935: Rbtree 树形DP


    Description

    给定一颗 N 个点的树,树上的每个点或者是红色,或者是黑色。
    每个单位时间内,你可以任选两个点,交换它们的颜色。
    出于某种恶趣味,你希望用最少的时间调整结点的颜色,使得对于每个点,离它最近的黑色点与它的距离不超过 x。
     

    Input

    输入的第一行包含整数 N 和 x(1 <= x <= 10^9)。
    接下来一行 N 个整数 C1-Cn,表示结点的初始颜色。1 表示黑色,0 表示红色。
    接下来 N-1 行,每行 3 个整数 ui, vi,wi(1 <= wi <= 10^9),表示点 ui 和 vi 之间存在权值为 wi的边。
     

    Output

    输出一个数表示答案;如果无解,输出 “-1”。
     

    我们令 $f[i][j][k]$ 表示 $i$ 的子树中有 $j$ 个黑点,且钦定 $i$ 与 $k$ 配对且 $i$ 子树其余点都配对好的最优解.

    特别地,我们还要钦定 $k$ 这个点先不进行任何操作.

    这道题难就难在这是一个匹配问题,有好多个点都需要去匹配,但是我们通过状态将问题缩小为只匹配一个点.

    我们可以这么设置状态的前提是因为我们发现以 $i$ 为根的子树中只有 $i$ 这个点有可能和子树外的点配对.

    因为你画一下图的话,你会发现如果 $i$ 的某个子孙要和 $i$ 为根子树外的点配对的话那个点一定和与 $i$ 配对的相等,否则不会更优.

    而其余情况,就是在 $i$ 的子树内部自行解决.

    我们还钦定不对 $k$ 进行操作的原因是这样就是两个状态的 $k$ 相同,也可以直接相加.

    我们需要先观察/挖掘题目中隐含的性质来简化问题,然后再去根据套路设置状态.

    #include <cstdio> 
    #include <cstring> 
    #include <algorithm>     
    #define N 508 
    #define setIO(s) freopen(s".in","r",stdin) 
    using namespace std;
    int n,K,edges,dfn,m;     
    int v[N],hd[N],to[N<<1],nex[N<<1],val[N<<1],Q[N];  
    int f[N][N][N],g[N][N],st[N],ed[N],dis[N][N],size[N];   
    void add(int u,int v,int c) 
    {
        nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=c;  
    }
    void getdis(int u,int ff,int y) 
    {  
        if(y==1) st[u]=++dfn,Q[dfn]=u;      
        for(int i=hd[u];i;i=nex[i])  if(to[i]!=ff) dis[y][to[i]]=dis[y][u]+val[i],getdis(to[i],u,y);   
        if(y==1) ed[u]=dfn;  
    }
    void dfs(int x,int ff) 
    {                     
        int i,j,k,mn,a; 
        for(a=1;a<=n;++a)  if(a!=x&&dis[a][x]<=K) f[x][0][a]=0;   
        f[x][1][x]=0;     
        size[x]=1; 
        for(i=hd[x];i;i=nex[i]) if(to[i]!=ff) 
        {
            int y=to[i]; 
            dfs(y,x); 
            memset(g,0x3f,sizeof(g));      
            for(j=0;j<=size[x]&&j<=m;++j) for(k=0;k<=size[y]&&k+j<=m;++k) 
            {
                mn=0x3f3f3f3f;   
                for(a=st[y];a<=ed[y];++a) mn=min(mn,f[y][k][Q[a]]+!v[Q[a]]);    
                for(a=1;a<=n;++a)    g[j+k][a]=min(g[j+k][a],f[x][j][a]+f[y][k][a]);   
                for(a=1;a<st[y];++a) g[j+k][Q[a]]=min(g[j+k][Q[a]],f[x][j][Q[a]]+mn);   
                for(a=ed[y]+1;a<=n;++a)  g[j+k][Q[a]]=min(g[j+k][Q[a]],f[x][j][Q[a]]+mn);   
            }
            size[x]+=size[y];  
            for(j=0;j<=size[x]&&j<=m;++j) for(a=1;a<=n;++a) f[x][j][a]=g[j][a];  
        }
    }
    int main() 
    { 
        // setIO("input"); 
        int i,j;    
        scanf("%d%d",&n,&K);      
        for(i=1;i<=n;++i) scanf("%d",&v[i]),m+=v[i];         
        for(i=1;i<n;++i) 
        {
            int x,y,z; 
            scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);  
        }   
        memset(f,0x3f,sizeof(f));           
        for(i=1;i<=n;++i) getdis(i,0,i);      
        dfs(1,0);   
        int ans=0x3f3f3f3f;       
        for(i=1;i<=n;++i) ans=min(ans,f[1][m][i]+!v[i]); 
        printf("%d
    ",ans>n?-1:ans); 
        return 0; 
    }
    

      

  • 相关阅读:
    Lock VS Monitor
    vue+element-ui路由配置相关
    数字与金额数字转换的正则表达式
    vue项目中多个入口的配置
    编写项目readme文件
    在vue中使用express-mock搭建mock服务
    编辑器——vscode
    【转】论前端的工程化
    vue学习
    node+express+http-proxy-middleware做代理
  • 原文地址:https://www.cnblogs.com/guangheli/p/12131522.html
Copyright © 2020-2023  润新知