Flash by sshockwave [树dp]
题目
给定一棵树,每个点有一个活动时间,长度为正整数$t_i$
你需要安排每个点的活动时间什么时候开始什么时候结束,并且满足:任何一个时刻没有两个相邻的点都在活动
开始时刻为0,在以上条件下最小化所有点的结束时间之和
$n \leq 2000$
思路
首先,给定的所有$t_i$都是正整数,说明答案一定是整数(这虽然很显然,但是很重要)
考虑某一个点什么时候开始
显然,最优的情况下它的开始时间可以被安排到和自己某个相邻点的结束时间相邻(或者它自己是在开始时刻就开始的)
又考虑到隔壁点也满足这一条,所以对于这个点来说,它的前驱时间(也就是在开始前的等待时间)一定可以被表示为从这个点开始的一条树链的时间和
注意上面的“可以被表示为”这样的表述:不是说只能这么安排,是说这样安排是可行的,而且方便我们设计算法
对于每个点$u$,计算数组$dis[u]$,表示排序去重后的从$u$出发的所有树链的长度
然后设$dp[u][i]$表示从根节点开始$dp$,在当前节点的子树中得到的结果:$dp[u][i]$表示在$u$开始之前已经经过了$dis[u][i]$这么长的等待时间
注意这里的$dp$并没有考虑父亲那边,而是只考虑了子树部分
因此在递归回到父亲那里做转移的时候,要这样操作:
t1[0]=t2[cnt[v]+1]=1e15;
for(i=1;i<=cnt[v];i++) t1[i]=min(t1[i-1],dp[v][i]);
for(i=cnt[v];i>=1;i--) t2[i]=min(t2[i+1],dp[v][i]);
l=0;r=0;
for(i=1;i<=cnt[u];i++){
while(dis[v][l+1]+w[v]<=dis[u][i]) l++;
while(dis[v][r]<dis[u][i]+w[u]) r++;
dp[u][i]+=min(t1[l],t2[r]);
}
做一个前后缀最小值,然后两边双指针进去搞到范围,然后再转移
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#define ll long long
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') flag=-1;
ch=getchar();
}
while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
int n,first[10010],cnte=-1;
struct edge{
int to,next;
}a[20010];int w[10010];
inline void add(int u,int v){
a[++cnte]=(edge){v,first[u]};first[u]=cnte;
a[++cnte]=(edge){u,first[v]};first[v]=cnte;
}
ll dis[2010][2010],lis[2010],dp[2010][2010];
int root,cnt[2010];
void getlis(int u,int f,ll d){
lis[++cnt[root]]=d;
// cout<<"getlis "<<u<<' '<<root<<' '<<d<<'\n';
int i,v;
for(i=first[u];~i;i=a[i].next){
v=a[i].to;if(v==f) continue;
getlis(v,u,d+w[v]);
}
}
void init(){
memset(first,-1,sizeof(first));
int i,num,t1,t2;
n=read();
for(i=1;i<n;i++){
t1=read();t2=read();
add(t1,t2);
}
for(i=1;i<=n;i++) w[i]=read();
for(i=1;i<=n;i++){
root=i;getlis(i,0,0);
sort(lis+1,lis+cnt[i]+1);
cnt[i]=unique(lis+1,lis+cnt[i]+1)-lis-1;
memcpy(dis[i]+1,lis+1,sizeof(ll)*(cnt[i]+1));
dis[i][0]=-1e15;
dis[i][cnt[i]+1]=1e15;
}
}
void dfs(int u,int f){
int i,v,j,l,r;
for(i=1;i<=cnt[u];i++) dp[u][i]=dis[u][i]+w[u];
for(i=first[u];~i;i=a[i].next){
v=a[i].to;if(v==f) continue;
dfs(v,u);
}
for(j=first[u];~j;j=a[j].next){
v=a[j].to;if(v==f) continue;
static ll t1[2010],t2[2010];
t1[0]=t2[cnt[v]+1]=1e15;
for(i=1;i<=cnt[v];i++) t1[i]=min(t1[i-1],dp[v][i]);
for(i=cnt[v];i>=1;i--) t2[i]=min(t2[i+1],dp[v][i]);
l=0;r=0;
for(i=1;i<=cnt[u];i++){
while(dis[v][l+1]+w[v]<=dis[u][i]) l++;
while(dis[v][r]<dis[u][i]+w[u]) r++;
dp[u][i]+=min(t1[l],t2[r]);
}
}
}
int main(){
init();
dfs(1,0);
int i;ll ans=1e15;
for(i=1;i<=cnt[1];i++) ans=min(ans,dp[1][i]);
cout<<ans<<'\n';
}
Flash by sshockwave [树dp]的更多相关文章
- CF456D A Lot of Games (字典树+DP)
D - A Lot of Games CF#260 Div2 D题 CF#260 Div1 B题 Codeforces Round #260 CF455B D. A Lot of Games time ...
- HDU4916 Count on the path(树dp??)
这道题的题意其实有点略晦涩,定义f(a,b)为 minimum of vertices not on the path between vertices a and b. 其实它加一个minimum ...
- Codeforces 219D. Choosing Capital for Treeland (树dp)
题目链接:http://codeforces.com/contest/219/problem/D 树dp //#pragma comment(linker, "/STACK:10240000 ...
- HDU4276 The Ghost Blows Light SPFA&&树dp
题目的介绍以及思路完全参考了下面的博客:http://blog.csdn.net/acm_cxlove/article/details/7964739 做这道题主要是为了加强自己对SPFA的代码的训练 ...
- Tsinsen A1219. 采矿(陈许旻) (树链剖分,线段树 + DP)
[题目链接] http://www.tsinsen.com/A1219 [题意] 给定一棵树,a[u][i]代表u结点分配i人的收益,可以随时改变a[u],查询(u,v)代表在u子树的所有节点,在u- ...
- HDU 3016 Man Down (线段树+dp)
HDU 3016 Man Down (线段树+dp) Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Ja ...
- bzoj 3572世界树 虚树+dp
题目大意: 给一棵树,每次给出一些关键点,对于树上每个点,被离它最近的关键点(距离相同被标号最小的)控制 求每个关键点控制多少个点 分析: 虚树+dp dp过程如下: 第一次dp,递归求出每个点子树中 ...
- bzoj 2286 [Sdoi2011]消耗战 虚树+dp
题目大意:多次给出关键点,求切断边使所有关键点与1断开的最小费用 分析:每次造出虚树,dp[i]表示将i和i子树与父亲断开费用 对于父亲x,儿子y ①y为关键点:\(dp[x]\)+=\(dismn( ...
- (纪念第一道完全自己想的树DP)CodeForces 219D Choosing Capital for Treeland
Choosing Capital for Treeland time limit per test 3 seconds memory limit per test 256 megabytes inpu ...
随机推荐
- 【mysql学习-1】
part-1: #use mysql;/*show tables;select * from user;use mysql;show databases;#create database db1; # ...
- [转]不让iTunes备份到c盘
很多人现在的C盘都是空间不大的SSD硬盘,ITUNES备份老是占越来越大的空间,不如动手把它改成其它盘好了.下面7个步骤教你转移备份. 1.需要一个小工具:Juction.exe,如果你已经是WIN7 ...
- Xshell4 出现Linux中中文字符乱码问题
Xshell5竟然收费了... 没办法只能用回Xshell4 但是不知道是版本不对还是在咋的 发现中文乱码,导致操作非常不方便 解决方案 LANG=zh_CN.big5 执行在终端执行上面的命令就可以 ...
- 洛谷 P3952
题目描述 小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序 ...
- JZOJ 5941. 乘
Sample Input Sample Input1: 4 3 9 6 5 8 7 7 Sample Output Sample Output1: 0做法(转自JZOJ):考虑 a 是定值, 而 b ...
- Hadoop(4)-Hadoop集群环境搭建
准备工作 开启全部三台虚拟机,确保hadoop100的机器已经配置完成 分发脚本 操作hadoop100 新建一个xsync的脚本文件,将下面的脚本复制进去 vim xsync #这个脚本使用的是rs ...
- python并发编程之多进程、多线程、异步、协程、通信队列Queue和池Pool的实现和应用
什么是多任务? 简单地说,就是操作系统可以同时运行多个任务.实现多任务有多种方式,线程.进程.协程. 并行和并发的区别? 并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任 ...
- GDOI DAY1游记
今天,是本蒟蒻的第一次参加GDOI,真激动! 今天,是GDOI第一天,昨天熬夜打代码,今天早上状态十分不好,于是... 进入了考场,叫我们自由打一会代码,于是...打了一坨AC机,重要的是错了(额.. ...
- itchat和matplotlib的结合使用爬取微信信息
前几天无意中看到了一片文章,<一件有趣的事:我用 Python 爬了爬自己的微信朋友>,这篇文章写的是使用python中的itchat爬取微信中朋友的信息,其中信息包括,昵称.性别.地理位 ...
- LI 标签中让文章标题左对齐,日期右对齐的方法
希望实现标题在左对齐,日期在右对齐,当直接给日期的span加上float:right时,IE8和FF都OK,但IE6/7则会换行,下面给出一个简单有效的解决办法. <!DOCTYPE html ...