上次说了说树形dp的入门

那么这次该来一点有难度的题目了:

UVA10859 Placing Lampposts

给定一个n个点m条边的无向无环图,在尽量少的节点上放灯,使得所有边都与灯相邻(被灯照亮)。

在灯的总数最小的前提下,被两盏灯同时照亮的边数应该尽可能大。

输入格式

第一行输入T,为数据组数。

每组数据第一行输入n,m,分别为该组数据中图的点数和边数。

以下m行,输入各边的两端点u,v。

输出格式

输出共T行。

对每组数据,一行输出三个数,最小灯数、被两盏灯同时照亮的边数、只被一盏灯照亮的边数。

n<=1000

有向无环图说白了就是一个森林(可以自己画图看看),第一问这不就是裸的树形dp求最大独立集吗?在每个森林上跑一遍树形dp就行。不过第二问第三问倒有点意思,怎么维护两边都放灯的道路的数量呢?这里介绍一个十分巧妙的方法,由于n<=1000,我们就可以把一个节点的权值设为比1000大的数,然后在转移的时候,如果这条路的两端节点没有都选,那么就+1,代表有多少只被一盏灯照亮的路,最后的答案除以k就是第一问,mod k就是第三问,用m减第三问的答案就是第二问。

void dfs(int x)
{
dp[x][]=k;//这里的k我们设为大于1000的数
dp[x][]=;
d[x]=;
for(int i=last[x];i;i=g[i].next)
{
int v=g[i].to;
if(d[v]) continue;
dfs(v);
dp[x][]+=min(dp[v][]+,dp[v][]);
dp[x][]+=dp[v][]+;//如果只被一盏灯照亮就加上1,目的是和被两盏灯同时照亮的边区分,同时也保证了被两盏灯同时照亮的边数应该尽可能大,毕竟我们取最小值。
    } 
}

经过这样一番神奇的操作,我们就成功的切掉了这道看似有点神仙的题目。

说了这么多,怎么能没有代码呢?

#include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<algorithm>
#define maxn 3005
using namespace std; struct edge
{
int next;
int to;
}g[maxn]; inline int read()
{
char c=getchar();
int res=,x=;
while(c<''||c>'')
{
if(c=='-')
x=-;
c=getchar();
}
while(c>=''&&c<='')
{
res=res*+(c-'');
c=getchar();
}
return x*res;
} int t,n,m,num,aa,bb,ans;
int k=;
int last[maxn],dp[maxn][],d[maxn]; inline void add(int from,int to)
{
g[++num].next=last[from];
g[num].to=to;
last[from]=num;
} void dfs(int x)
{
dp[x][]=k;
dp[x][]=;
d[x]=;
for(int i=last[x];i;i=g[i].next)
{
int v=g[i].to;
if(d[v]) continue;
dfs(v);
dp[x][]+=min(dp[v][]+,dp[v][]);
dp[x][]+=dp[v][]+;
}
} int main()
{
t=read();
while(t--)
{
n=read();m=read();
num=;ans=;
memset(last,,sizeof(last));
memset(dp,,sizeof(dp));
memset(d,,sizeof(d));
for(int i=;i<=m;i++)
{
aa=read();bb=read();
add(aa,bb);
add(bb,aa);
}
for(int i=;i<=n;i++)
{
if(!d[i])
{
dfs(i);
ans+=min(dp[i][],dp[i][]);
}
}
printf("%d %d %d\n",ans/k,m-(ans%k),ans%k);
}
}

下面再来看这样的一道简(shen)单(xian)题

UVA1220 Hali-Bula的晚会 Party at Hali-Bula

公司里有n(n<=200)个人形成一个树状结构,即除了老板之外每个员工都有唯一的直属上司。要求选尽量多的人,但不能同时选择一个人和他的直属上司。问:最多能选多少人,以及在人数最多的前提下方案是否唯一。

输入:第一行一个数n;第二行输入老板的名字;以下的n-1行中,每行是一位员工的名字和其直属上司的名字(英文单词,长度为1到100),两个名字之间有空格隔开,'0'为输入结束的标识符。

输出:一行,输出一个数字,表示最大的访客数量。并再同一行输出单词'Yes'或'No',代表目前方案是否唯一。

这个的第一问好像有点简单的样子,但是这第二问好像有点毒瘤啊。我们不妨从状态转移上入手,

dp[x][]+=dp[v][];
dp[x][]+=max(dp[v][],dp[v][]);

不难发现,如果 dp[v][1]==dp[v][0] 那么不就会出现两种方式了吗,因此我们用c数组来维护一下方案书是否唯一就行了,我们先判断孩子的方案数是否唯一,再用孩子去更新父亲,因为如果孩子的方案数不唯一,那么由这个孩子转移后的父亲肯定方案数也不唯一,这样就可以愉快的树形dp了。

像这样:

    if(dp[v][]>dp[v][]&&c[v][])
{
c[x][]=;
}
if(dp[v][]>dp[v][]&&c[v][])
{
c[x][]=;
}
if(dp[v][]==dp[v][])
{
c[x][]=;
}
if(c[v][])
{
c[x][]=;
}

最后怎么少得了完整ac代码呢?

 #include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<algorithm>
#include<map>
#define maxn 2005
using namespace std; struct edge
{
int next;
int to;
}g[maxn]; inline int read()
{
char c=getchar();
int res=,x=;
while(c<''||c>'')
{
if(c=='-')
x=-;
c=getchar();
}
while(c>=''&&c<='')
{
res=res*+(c-'');
c=getchar();
}
return x*res;
} int n;
string aa,bb,root;
int cnt;
int num;
int last[maxn],dp[maxn][],d[maxn],c[maxn][];
map<string,int>a; inline void add(int from,int to)
{
g[++num].next=last[from];
g[num].to=to;
last[from]=num;
} void dfs(int x)
{
d[x]=;
dp[x][]=;
dp[x][]=;
for(int i=last[x];i;i=g[i].next)
{
int v=g[i].to;
if(!d[v])
{
dfs(v);
dp[x][]+=dp[v][];
dp[x][]+=max(dp[v][],dp[v][]);
if(dp[v][]>dp[v][]&&c[v][])
{
c[x][]=;
}
if(dp[v][]>dp[v][]&&c[v][])
{
c[x][]=;
}
if(dp[v][]==dp[v][])
{
c[x][]=;
}
if(c[v][])
{
c[x][]=;
}
}
}
} int main()
{
while()
{
n=read();
if(n==) break;
cnt=;num=;
memset(last,,sizeof(last));
memset(dp,,sizeof(dp));
memset(d,,sizeof(d));
memset(c,,sizeof(c));
a.clear();
for(int i=;i<=n;i++)
{
if(i==)
{
cin>>root;
a[root]=++cnt;
}
else
{
cin>>aa>>bb;
if(!a[aa])
{
a[aa]=++cnt;
}
if(!a[bb])
{
a[bb]=++cnt;
}
add(a[aa],a[bb]);
add(a[bb],a[aa]);
}
}
dfs();
printf("%d ",max(dp[][],dp[][]));
if(dp[][]==dp[][]||(dp[][]<dp[][]&&c[][])||(dp[][]>dp[][]&&c[][]))
printf("No\n");
else printf("Yes\n");
}
}

No man or woman is worth your tears, and the one who is, won't make you cry.

--snowy

2019-01-15    18:48:21

再谈树形dp的更多相关文章

  1. POJ 3659 再谈树形DP

    Cell Phone Network Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 5325   Accepted: 188 ...

  2. 再探树形dp

    随着校oj终于刷进了第一页,可以不用去写那些水题了,开始认真学习自己的东西,当然包括文化课.努力.. 这道题呢是道树形dp,可看到了根本就不知道怎么写思考过程: 5min 终于看懂了题 画了样例的图把 ...

  3. Codeforces Beta Round #14 (Div. 2) D. Two Paths 树形dp

    D. Two Paths 题目连接: http://codeforces.com/contest/14/problem/D Description As you know, Bob's brother ...

  4. Codeforces 919D Substring (拓扑排序+树形dp)

    题目:Substring 题意:给你一个有向图, 一共有n个节点 , m条变, 一条路上的价值为这个路上出现过的某个字符最多出现次数, 现求这个最大价值, 如果价值可以无限大就输出-1. 题解:当这个 ...

  5. 浅谈关于树形dp求树的直径问题

    在一个有n个节点,n-1条无向边的无向图中,求图中最远两个节点的距离,那么将这个图看做一棵无根树,要求的即是树的直径. 求树的直径主要有两种方法:树形dp和两次bfs/dfs,因为我太菜了不会写后者这 ...

  6. 树形DP 学习笔记

    树形DP学习笔记 ps: 本文内容与蓝书一致 树的重心 概念: 一颗树中的一个节点其最大子树的节点树最小 解法:对与每个节点求他儿子的\(size\) ,上方子树的节点个数为\(n-size_u\) ...

  7. BZOJ 2286 消耗战 (虚树+树形DP)

    给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...

  8. NOIP2011pj表达式的值[树形DP 笛卡尔树 | 栈 表达式解析]

    题目描述 对于1 位二进制变量定义两种运算: 运算的优先级是: 先计算括号内的,再计算括号外的. “× ”运算优先于“⊕”运算,即计算表达式时,先计算× 运算,再计算⊕运算.例如:计算表达式A⊕B × ...

  9. 【BZOJ-2286】消耗战 虚树 + 树形DP

    2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status] ...

随机推荐

  1. luogu P3237 [HNOI2014]米特运输

    传送门 谢特运输 先要搞懂题目是什么意思,简化版题意就是一棵有根树,要使得每个点都满足任意一个儿子的权值等于这个点权值除以儿子数量,问最少要修改多少个点的点权 就可以一边dfs求出每个点权值是点1的多 ...

  2. Windows下开启composer镜像服务来安装yii

    网上关于使用composer的安装教程挺多的,但是作为新手的我,觉得好凌乱,不断尝试后,终于安装好了.最后总结出,用开启composer的镜像服务来安装yii是最好的啦,当然,归档文件的做法有利有弊就 ...

  3. python的进程/线程/协程

    1.python的多线程 多线程就是在同一时刻执行多个不同的程序,然而python中的多线程并不能真正的实现并行,这是由于cpython解释器中的GIL(全局解释器锁)捣的鬼,这把锁保证了同一时刻只有 ...

  4. SFTP免密码登录原理

    概述 Public Key认证的主要魅力在于认证时承诺不必提供密码就能够同远程系统建立连接. Public Key认证的基础在于一对密钥,public key和private key,public k ...

  5. react-踩坑记录——页面底部多出一倍高度的空白

    挂载slider组件后页面底部多出一倍高度的空白,如下: slider组件内容⬇️: class Slider extends Component{ constructor(){ super(); } ...

  6. J - Joyful HDU - 5245 (概率)

    题目链接: J - Joyful  HDU - 5245 题目大意:给你一个n*m的矩阵,然后你有k次涂色机会,然后每一次可以选定当前矩阵的一个子矩阵染色,问你这k次用完之后颜色个数的期望. 具体思路 ...

  7. Android 5.0以上Material Design 沉浸式状态栏

    偶然在知乎上看到这个问题,Android 5.0 如何实现将布局的内容延伸到状态栏,之前也见过多个应用的这个功能,但是知乎上的答案却没有一个真正实现此功能的一类是把标题栏设置App主题颜色,一类是提取 ...

  8. LwIP Application Developers Manual1---介绍

    1.前言 本文主要是对LwIP Application Developers Manual的翻译 2.读者(应用开发手册的读者) 谁适合读这份手册 网络应用的开发者 想了解lwIP的网络应用开发者 阅 ...

  9. Python运维开发基础06-语法基础【转】

    上节作业回顾 (讲解+温习120分钟) #!/usr/bin/env python3 # -*- coding:utf-8 -*- # author:Mr.chen # 添加商家入口和用户入口并实现物 ...

  10. whistle工具全程入门

    接触过前后端开发的同学应该都了解网络请求代理工具fiddler(mac下面常用的是Charles),可以用来拦截分析请求.包装请求.本地调试和移动端代理开发调试等.多多少少,fiddler和Charl ...