BZOJ 4346: [POI2016]Nadajniki

树形DP

好久没写正常的题解了,于是写一发。

刚开始想这题的转移的时候乱糟糟的,因为一个点的儿子可能很多,转移起来就很麻烦。其实树形DP的本质是有一棵树,每次只加入一棵子树。这样想起来就方便多了。

记f[i][j=0/1/2/3/4][k=0/1/2]表示以i为根的子树,j=0表示i和i子树中与i相邻的点都没有放,j=1和j=2分别表示i这个点放了1或2个,j=3和j=4分别表示i这个点没放但i子树中与i相邻的点放了1或2个,k表示与i相邻的点中还缺了几个,即至少还要放几个。

最麻烦的地方就是状态之间的转移,手推清楚即可。

#include<cstdio>
#include<algorithm>
#define N 200005
using namespace std;
const int INF = 1 << 29;
int n, last[N], ecnt, f[N][5][3], h[5][3];
struct edge{int next, to;}e[N<<1];
void addedge(int a, int b)
{
    e[++ecnt] = (edge){last[a], b};
    last[a] = ecnt;
}
bool trans(int j, int k, int a, int b, int &x, int &y)
{
    if(b == 1 && j != 1 && j != 2) return false;
    if(b == 2 && j != 2) return false;
    int nk;
    if(j == 1 || j == 2 || j == 4 || a == 1 || a == 2 || a == 4) nk = 0;
    else if(j == 3 && a == 3) nk = 0;
    else if(j == 0 && a == 3) nk = 1;
    else if(j == 3 && a == 0) nk = 1;
    else nk = 2;
    if(a == 1) k-=1;
    else if(a == 2) k-=2;
    y = max(k, nk);

    if(j == 1 || j == 2) x = j;
    else
    {
        if(j == 0) x = 2;
        else x = j;
        if(a == 1) x += 1;
        else if(a == 2) x += 2;
        if(x == 2) x = 0;
        else x = min(x, 4);
    }
    return true;
}
void dfs(int x, int fa)
{
    for(int j = 0; j < 5; j++) for(int k = 0; k < 3; k++) f[x][j][k] = INF;
    f[x][0][0] = 0; f[x][1][0] = 1; f[x][2][0] = 2;
    for(int i = last[x]; i; i = e[i].next)
    {
        int y = e[i].to; if(y == fa) continue;
        dfs(y, x);
        for(int j = 0; j < 5; j++) for(int k = 0; k < 3; k++) h[j][k] = INF;
        for(int j = 0; j < 5; j++) for(int k = 0; k < 3; k++) if(f[x][j][k] < INF)
            for(int a = 0; a < 5; a++) for(int b = 0; b < 3; b++) if(f[y][a][b] < INF)
            {
                int c, d;
                if(trans(j, k, a, b, c, d))
                    h[c][d] = min(h[c][d], f[x][j][k] + f[y][a][b]);
            }
        for(int j = 0; j < 5; j++) for(int k = 0; k < 3; k++) f[x][j][k] = h[j][k];
    }
}
int main()
{
    scanf("%d",&n);
    for(int i = 1; i < n; i++)
    {
        int a, b; scanf("%d%d",&a,&b);
        addedge(a, b); addedge(b, a);
    }
    dfs(1,0);
    int ans = INF;
    for(int j = 0; j < 5; j++) ans = min(ans, f[1][j][0]);
    printf("%d\n",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值