zoukankan      html  css  js  c++  java
  • POJ3763 Tour in Wonder Land

    一道很不错的树状DP题,题意是求向一颗树,无向的,中加最少的边使其有哈密顿回路。

    刚开始以为是张普通的图,还想着那样也可以树状DP呢。主要的想法是将一个回路拆开来看,想是一条链,然后先求一颗树中加最少的边,记做K,使得其有一条哈密顿链,然后最后再向起点和终点加一条边,答案就是K+1。并且对于这样分析子树哈密顿链的方法,来扩展想要的状态的答案。对于这样分析,子树链的状态有两种情况,先给定一个方向,即有个根,那么一种是以根为起点,终止于一个非根节点,记做dp[i][0],另一种状态是以一个非根节点为起点,另一个非根节点为终点,记做dp[i][1]。对于这样的状态设计,可以进行父节点与子节点间的状态转移。但对于这样的状态设计,转移起来,比较麻烦。因此把第一种状态归并到第二种状态中更新为最新的第二种状态。
    因此可以这样转移:
    dp[u][0] = min{dp[v][0] + dp[1..son(u) except for v][1] + number(son) - 1}
    dp[u][1] = min{dp[v][0] + dp[w][0] + dp[1..son(u) except for v & w][1] + number(son) - 2}
    dp[u][1] = min{dp[u][0], dp[u][1]}
    对于上述的状态转移,其实在dp[u][1]中,还有别的状态可能转移过来,但这里可以通过数学归纳法和普通到特殊的方法来证明这样的状态是不会比之前设计的状态转移过来所更优(但经过实验发现是错的)。可以从贪心的角度看,第一种的转态转移理解成,必须走根到儿子的一条边,要不浪费了,第二种情况同理。
    树状深搜时,可以通过向dfs中添加父标记来操作。可以通过累加dp[son][1]的和来简化状态转移时的操作。
    感谢:
    http://blog.csdn.net/yuhailin060/archive/2010/05/10/5576255.aspx
    http://blog.csdn.net/ccsu_001/archive/2010/05/10/5576386.aspx

    代码
    #include <iostream>
    #include
    <string>
    #include
    <vector>
    using namespace std;

    const int MAX = 100005;

    int n;
    int dp[MAX][2];

    vector
    <vector<int> > mm;

    void dfs(int u, int f)
    {
    dp[u][
    0] = dp[u][1] = -1;
    int sz = mm[u].size();
    int son = 0, all = 0;
    for(int i = 0; i < sz; i++)
    {
    int v = mm[u][i];
    if(v != f)
    {
    dfs(v, u);
    all
    += dp[v][1];
    son
    ++;
    }
    }
    if(son == 0) dp[u][0] = dp[u][1] = dp[u][2];
    else
    {
    //说明不是叶子
    //get dp[u][0]
    for(int i = 0; i < sz; i++)
    {
    int v = mm[u][i];
    if(v != f)
    {
    int t = dp[v][0] + all - dp[v][1] + son - 1;
    if(dp[u][0] == -1 || t < dp[u][0])
    dp[u][
    0] = t;
    }
    }
    //get dp[u][1]
    for(int i = 0; i < sz; i++) for(int j = i + 1; j < sz; j++)
    {
    int v = mm[u][i], w = mm[u][j];
    if(v != f && w != f)
    {
    int t = dp[v][0] + dp[w][0] + all - dp[v][1] - dp[w][1] + son - 2;
    if(dp[u][1] == -1 || t < dp[u][1])
    dp[u][
    1] = t;
    }
    }
    if(dp[u][1] == -1 || dp[u][0] < dp[u][1])
    dp[u][
    1] = dp[u][0];
    }
    //s[u] = 1;
    }

    int main()
    {
    while(scanf("%d", &n) != EOF)
    {
    mm.clear();
    mm.resize(n
    + 1);
    for(int i = 1; i < n; i++)
    {
    int a, b;
    scanf(
    "%d%d", &a, &b);
    mm[a].push_back(b);
    mm[b].push_back(a);
    }
    dfs(
    1, -1);
    printf(
    "%d\n", dp[1][1] + 1);
    }
    }
  • 相关阅读:
    分布式锁-数据库实现
    MyBatis-Plus自动填充功能失效导致原因
    Java中锁的解决方案
    2:什么是单体应用锁?什么是分布式锁?
    1:初始锁这个概念
    移动端网页开发问题小结
    node.js+socket.io创建web聊天室
    使用HTML5实现刮刮卡效果
    总结(活动)
    videoJs 使用
  • 原文地址:https://www.cnblogs.com/litstrong/p/1786415.html
Copyright © 2011-2022 走看看