☆【树型动态规划】巡逻

原创 2012年03月23日 17:56:22

【问题描述】 
在一个地区中有 n个村庄,编号为1, 2, ..., n。有n – 1条道路连接着这些村
庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其
他任一个村庄。每条道路的长度均为 1个单位。 
为保证该地区的安全,巡警车每天要到所有的道路上巡逻。警察局设在编号
为1的村庄里,每天巡警车总是从警察局出发,最终又回到警察局。 
下图表示一个有8个村庄的地区,其中村庄用圆表示(其中村庄 1用黑色的
圆表示),道路是连接这些圆的线段。为了遍历所有的道路,巡警车需要走的距
离为14 个单位,每条道路都需要经过两次。 

为了减少总的巡逻距离,该地区准备在这些村庄之间建立 K 条新的道路,
每条新道路可以连接任意两个村庄。两条新道路可以在同一个村庄会合或结束
(见下面的图例(c) )。一条新道路甚至可以是一个环,即,其两端连接到同一
个村庄。 
由于资金有限,K 只能是 1或2。同时,为了不浪费资金,每天巡警车必须
经过新建的道路正好一次。 
下图给出了一些建立新道路的例子: 


在(a)中,新建了一条道路,总的距离是 11。在(b)中,新建了两条道路,总
的巡逻距离是 10。在(c)中,新建了两条道路,但由于巡警车要经过每条新道路
正好一次,总的距离变为了 15。 
试编写一个程序,读取村庄间道路的信息和需要新建的道路数,计算出最佳
的新建道路的方案使得总的巡逻距离最小,并输出这个最小的巡逻距离。 

【输入格式】 
第一行包含两个整数 n, K(1 ≤ K ≤ 2)。接下来 n – 1行,每行两个整数 a, b,
表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n)。 
【输出格式】 
输出一个整数,表示新建了K 条道路后能达到的最小巡逻距离。 
【样例输入1】 
8 1 
1 2 
3 1 
3 4 
5 3 
7 5 
8 5 
5 6 
【样例输出 1】 
11 
【样例输入2】 
8 2 
1 2 
3 1 
3 4 
5 3 
7 5 
8 5 
5 6 
【样例输出 2】 
10 
【样例输入3】 
5 2 
1 2 
2 3 
3 4 
4 5 
【样例输出 3】 
6 
【数据范围】 
10%的数据中,n ≤ 1000,    K = 1; 
30%的数据中,K = 1; 
80%的数据中,每个村庄相邻的村庄数不超过 25; 
90%的数据中,每个村庄相邻的村庄数不超过 150; 
100%的数据中,3 ≤ n ≤ 100,000, 1 ≤ K ≤ 2。
此题可以用树形动态规划解决。
状态:f[u][j][k]表示u这棵子树中,共有j条完整的链加上k / 2条伸出到其它树根的链(这样的链算半条)。
转移过程见程序注释。

Accode:

#include <cstdio>
#include <cstdlib>
#include <algorithm>

using std::max;
const char fi[] = "patrol.in";
const char fo[] = "patrol.out";
const int maxN = 100010;

struct Edge {int v; Edge *next;} *edge[maxN];
int f[maxN][3][2], tmp[3][2], n, K;

void init_file()
{
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
    return;
}

inline int getint()
{
    int res = 0; char tmp;
    while (!isdigit(tmp = getchar()));
    do res = (res << 3) + (res << 1) + tmp - '0';
    while (isdigit(tmp = getchar()));
    return res;
}

inline void insert(int u, int v)
{
    Edge *p = new Edge;
    p -> v = v;
    p -> next = edge[u];
    edge[u] = p;
    return;
}

void readdata()
{
    n = getint(); K = getint();
    for (int i = 1; i < n; ++i)
    {
        int u = getint(), v = getint();
        insert(u, v); insert(v, u);
    }
    return;
}

void DP(int u, int Last)
{
    for (Edge *p = edge[u]; p; p = p -> next)
    if (p -> v != Last)
    {
        int v = p -> v; DP(v, u);
        memcpy(tmp, f[u], sizeof tmp);
        for (int j = 0; j < K + 1; ++j)
        for (int k = 0; j + k < K + 1; ++k)
        {
            f[u][j + k][0] = max(f[u][j + k][0],
                f[v][j][0] + tmp[k][0]);
	//没有半条链的情况。
            f[u][j + k][1] = max(f[u][j + k][1],
                f[v][j][0] + tmp[k][1]);
	//有半条链但在这棵树本身的情况。
            f[u][j + k][1] = max(f[u][j + k][1],
                f[v][j][1] + tmp[k][0] + 1);
	//其子树中有半条链的情况
	//(这时子树的半条链的长度要计入总长度,
	//但不计入总链数)。
            if (j + k < K) f[u][j + k + 1][0] =
                max(f[u][j + k + 1][0],
                    f[v][j][1] + tmp[k][1] + 1);
	//子树中的半条链连到根(不一定是根,
	//也有可能是该树的其它节点)的情况,
	//此时链的总数要+1。
        }
    }
    return;
}

void work()
{
    DP(1, 0);
    printf("%d\n", ((n - 1) << 1) + K -
            max(f[1][1][0], f[1][K][0]));
	//结果等于总边数的2倍减去求得的最长链
	//(此链不用重复走,所以被减去)
	//再加上新添的边的条数
	//(新添的边必须被经过)。
    return;
}

int main()
{
    init_file();
    readdata();
    work();
    return 0;
}

第二次做:

#include <cstdio>
#include <cstdlib>
#include <string>
#include <algorithm>
#define max(a, b) ((a) > (b) ? (a) : (b))

const int maxN = 100010;
struct Edge{int v; Edge *next;} *edge[maxN];
int f[maxN][3][2], pf[3][2], n, K;

void Dp(int u, int Last)
{
    for (Edge *p = edge[u]; p; p = p -> next)
    if (p -> v != Last)
    {
        Dp(p -> v, u);
        int v = p -> v;
        memcpy(pf, f[u], sizeof pf);
        //注意这里不能用成局部变量,否则爆栈。 
        for (int j = 0; j < K + 1; ++j)
        for (int k = 0; j + k < K + 1; ++k)
        {
            f[u][j + k][0] = max(f[u][j + k][0],
                pf[j][0] + f[v][k][0]);
            f[u][j + k][1] = max(f[u][j + k][1],
                pf[j][1] + f[v][k][0]);
            f[u][j + k][1] = max(f[u][j + k][1],
                pf[j][0] + f[v][k][1] + 1);
            if (j + k < K) f[u][j + k + 1][0]
                = max(f[u][j + k + 1][0],
                pf[j][1] + f[v][k][1] + 1);
        }
    }
    return;
}

inline int getint()
{
    int res = 0; char tmp;
    while (!isdigit(tmp = getchar()));
    do res = (res << 3) + (res << 1) + tmp - '0';
    while (isdigit(tmp = getchar()));
    return res;
}

inline void Ins(int u, int v)
{
    Edge *p = new Edge; p -> v = v;
    p -> next = edge[u]; edge[u] = p;
    return;
}

int main()
{
    freopen("patrol.in", "r", stdin);
    freopen("patrol.out", "w", stdout);
    n = getint(); K = getint();
    for (int i = 1; i < n; ++i)
    {
        int u = getint(), v = getint();
        Ins(u, v); Ins(v, u);
    }
    Dp(1, 0);
    int ans = max(f[1][1][0], f[1][K][0]);
    printf("%d\n", (n << 1) + K - 2 - ans); //
    return 0;
}

树形动态规划(树状DP)小结

树状动态规划定义 之所以这样命名树规,是因为树形DP的这一特殊性:没有环,dfs是不会重复,而且具有明显而又严格的层数关系。利用这一特性,我们可以很清晰地根据题目写出一个在树(型结构)上的记忆化搜索...
  • txl16211
  • txl16211
  • 2015年04月29日 23:10
  • 9148

HDU 动态规划(46道题目)

HDU 动态规划(46道题目) 原作者网址:http://www.cppblog.com/doer-xee/archive/2010/01/28/102629.html#Post 先标记 以后慢慢...
  • briup_acmer
  • briup_acmer
  • 2015年01月15日 10:10
  • 1732

[NOIP2003]加分二叉树【动态规划】

【问题描述】 设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第j个节点的分数为di,tree及它的每个子...
  • Tag_king
  • Tag_king
  • 2015年01月19日 20:41
  • 1418

动态规划-树型DP经典课件

  • 2016年07月06日 09:56
  • 4.26MB
  • 下载

树型动态规划

  • 2012年12月01日 17:03
  • 116KB
  • 下载

【jzoj1010】【CQOI2009】【叶子的颜色】【树型动态规划】

题目大意 给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(...
  • chunkitlau
  • chunkitlau
  • 2017年05月15日 21:49
  • 76

树型动态规划练习题:【bzoj1017】[JSOI2008]魔兽地图DotR

Description DotR (Defense of the Robots) Allstars是一个风靡全球的魔兽地图,他的规则简单与同样流行的地图DotA (Defense of the ...
  • NOI2015
  • NOI2015
  • 2015年03月19日 21:57
  • 408

皇宫看守 树型动态规划

题目大意   太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。  皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人...
  • A_loud_name
  • A_loud_name
  • 2016年05月12日 18:17
  • 911

二*苹果树 树型动态规划

题目大意   对于一个二叉树,除根节点外,每个节点都有相应的一个权值。在此基础上,求保留多少个点使得其仍然满足树的性质且权值总和最大。   分析   先建树。   ch[v,1],ch[v,...
  • A_loud_name
  • A_loud_name
  • 2016年05月05日 20:42
  • 414

动态规划----树型DP----树的最大独立集

一、树型DP的概念 树型DP即在树上进行DP。 树是无环图,顺序可以是从叶子到根节点,也可以从根到叶子节点。 一般树型DP的特征很明显,即状态可以表示为树中的节点,每个节点的状态可以由其子节点状...
  • C20180602_csq
  • C20180602_csq
  • 2017年04月24日 14:05
  • 680
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:☆【树型动态规划】巡逻
举报原因:
原因补充:

(最多只允许输入30个字)