【uva1380 - 一个调度问题】思路题+树形dp
【题意】 有n<=200个恰好需要一天完成的任务,要求用最少的时间完成所有任务。任务可以同时完成。但是有一些约束,分有向和无向两种,其中A-->B表示A必须在B前面完成,而A--B表示A和B不能在同一天完成。
题解:最具体的题解在紫书上。。。
如果树上的所有边都是有向边,那么答案就是最长链上的点数。
这个显然。。因为A-->B--->C---->D就最少需要四天。。
这样,原问题转化为:将树上所有的无向边定向,使得树上的最长链最短。
最长链最短——二分答案。
现在问题再次转化:给定一个x,判断是否可以给无向边定向,使得最长链的点数<=x。

设定f[i]表示以i为根的子树全部定向后,最长链点数不超过x的前提下,后代到i的最长链的最小值。g[i]相反(i到后代)。
设y为i的某个孩子。
转化无向边的过程分为两种情况:
a. 如果一个子树中不存在无向边,则经过子树的根结点的最长有向路结点数为f+g+1。
b. 如果一个结点a的子结点存在无向边,则我们先递归求出所有a的子结点的f和g,然后暴力枚举将所有无向边转化为上行/下行有向边,对于每一种枚举,按照上面不存在无向边时的方法,求出f, g和f+g+1,检查是否大于k。枚举的过程中,记下所有成功的枚举中最小的f和g,把它们和原本就是有向边子结点的最小的f和g比较,取较大值。(f和g本身是最长路的长度,这里要取的是不同成功的枚举情况下,遇到的最小的f和g。)
如何给边定向呢?
但是如果我们完全枚举无向边的转换方法,则复杂度为O(2^n),n为子结点中无向边的数量。这里有一个非常棒的优化:
求出所有子结点的f和g之后,把无向边的f和g值存到一个数组中,按照f值排序。
之后我们从第一位开始考虑,将无向边换为下行有向边,考虑到第p位的时候,我们可以将前p-1位的无向边一并换为下行有向边。因为第p位的f值大于前p-1位无向边的f值,将前p-1位同时换为下行有向边,整个树的f值不会变(f为最长路的长度,而排序后,前p-1位形成的路都不会比第p位长),而g值有可能变小。这时,我们找出第p+1位到第n位中最大的g值,求出f+g+1,检查是否小于等于k即可。一旦有某一个p满足了要求就可以得出结果并终止枚举。复杂度为线性。
也就是说,我们只需要枚举p,把f值前小的全部定向为y-->i。
求g的过程相同,按照g值排序、枚举即可。
为什么取的是不同情况下遇到的最小的f和g呢?
因为对于以后要用到f[i]和g[i]的点,必定是i的父亲,它每次只用到f[i]或g[i]。
但是对于根节点而言,它的最长链是f[i]+g[i],这里的f[i]和g[i]取了不同的情况,所以我们在求f[i]、g[i]的时候就看ff+gg+1是否<=lim,是的话才更新。也就是说,如果没有一种方案满足,f[i]和g[i]都是INF。
所以最后判断是否可行只需要看根节点的f[i]或g[i]是否<INF即可。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std; const int N=,INF=(int)1e9;
int n,len,tl,f[N],g[N],first[N];
struct node{
int x,y,next,tmp;
bool bk;
}a[N];
struct point{
int d,id;
}tf[N],tg[N]; int maxx(int x,int y){return x>y ? x:y;}
int minn(int x,int y){return x<y ? x:y;} void ins(int x,int y,int tmp)
{
a[++len].x=x;a[len].y=y;a[len].tmp=tmp;a[len].bk=;
a[len].next=first[x];first[x]=len;
} bool cmp(point x,point y){return x.d<y.d;} void dp(int x,int lim)
{
for(int i=first[x];i;i=a[i].next) dp(a[i].y,lim);
tl=;
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(a[i].tmp==)
{
tl++;
tf[tl].d=f[y];tf[tl].id=i;
tg[tl].d=g[y];tg[tl].id=i;
}
}
sort(tf+,tf++tl,cmp);
sort(tg+,tg++tl,cmp);
if(tl==)
{
f[x]=;g[x]=;
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(a[i].tmp==) f[x]=maxx(f[x],f[y]+);
if(a[i].tmp==) g[x]=maxx(g[x],g[y]+);
if(f[x]+g[x]+>lim) {f[x]=INF,g[x]=INF;break;}
}
if(!first[x]) f[x]=g[x]=INF;//debug
}
else
{
//find_f
f[x]=INF;g[x]=INF;
int ff,gg;
for(int k=;k<=tl;k++)
{
ff=;gg=;
if(k>) a[tf[k].id].bk=;
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(a[i].tmp== || (a[i].tmp== && a[i].bk==)) ff=maxx(ff,f[y]+);
if(a[i].tmp== || (a[i].tmp== && a[i].bk==)) gg=maxx(gg,g[y]+);
}
if(ff+gg+<=lim) f[x]=minn(f[x],ff);
}
for(int k=;k<=tl;k++) a[tf[k].id].bk=; for(int k=;k<=tl;k++)
{
ff=;gg=;
if(k>) a[tg[k].id].bk=;
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(a[i].tmp== || (a[i].tmp== && a[i].bk==)) ff=maxx(ff,f[y]+);
if(a[i].tmp== || (a[i].tmp== && a[i].bk==)) gg=maxx(gg,g[y]+);
}
if(ff+gg+<=lim) g[x]=minn(g[x],gg);
}
for(int k=;k<=tl;k++) a[tg[k].id].bk=;
}
// printf("f %d = %d g %d = %d\n",x,f[x],x,g[x]);
} bool check(int lim)
{
// printf("lim = %d\n",lim);
dp(,lim);
return (f[]<INF);
} int main()
{
freopen("a.in","r",stdin);
int n,y;
char ch;
while()
{
n=;
len=;
memset(first,,sizeof(first));
while()
{
int x;
scanf("%d",&x);
if(x==) break;
n=maxx(n,x);
while()
{
scanf("%d%c",&y,&ch);
n=maxx(n,y);
if(y==) break;
if(ch=='d') ins(x,y,);
else if(ch=='u') ins(x,y,);
else ins(x,y,);
}
}
if(n==) break;
int l=,r=n,mid;//debug 是n而不是n-1
while(l<r)
{
mid=(l+r)/;
if(check(mid)) r=mid;
else l=mid+;
}
printf("%d\n",l);
}
return ;
}
【uva1380 - 一个调度问题】思路题+树形dp的更多相关文章
- POJ-3659-最小支配集裸题/树形dp
Cell Phone Network Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 7127 Accepted: 254 ...
- noiac132 B君的第三题 (树形dp)
传送门 本来想用点分治做,结果root又求不对 算的时候还算错了 我好菜啊 结果szr大佬告诉我是树形dp 我好菜啊!! 我们有$\lceil \frac{x}{k} \rceil = \frac{x ...
- HDU 4679 Terrorist’s destroy (2013多校8 1004题 树形DP)
Terrorist’s destroy Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Othe ...
- (树形DP入门题)Anniversary party(没有上司的舞会) HDU - 1520
题意: 有个公司要举行一场晚会.为了让到会的每个人不受他的直接上司约束而能玩得开心,公司领导决定:如果邀请了某个人,那么一定不会再邀请他的直接的上司,但该人的上司的上司,上司的上司的上司等都可以邀请. ...
- 【DP_树形DP专题】题单总结
转载自 http://blog.csdn.net/woshi250hua/article/details/7644959#t2 题单:http://vjudge.net/contest/123963# ...
- 模拟赛:树和森林(lct.cpp) (树形DP,换根DP好题)
题面 题解 先解决第一个子问题吧,它才是难点 Subtask_1 我们可以先用一个简单的树形DP处理出每棵树内部的dis和,记为dp0[i], 然后再用一个换根的树形DP处理出每棵树内点 i 到树内每 ...
- 树形DP和状压DP和背包DP
树形DP和状压DP和背包DP 树形\(DP\)和状压\(DP\)虽然在\(NOIp\)中考的不多,但是仍然是一个比较常用的算法,因此学好这两个\(DP\)也是很重要的.而背包\(DP\)虽然以前考的次 ...
- 树形DP总结,持续更新
自己做了动态规划的题目已经有了一个月,但是成效甚微,所以来总结一下动态规划,希望自己能够温故知新.这个博客是关于树形dp的,动态规划的一类题目. 首先从最简单的树形DP入手,树形DP顾名思义就是一棵树 ...
- 树形dp hdu-4616-Game
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4616 题目大意: 给一棵树,每个节点有一个礼物值及是否有trick,每来到一个节点必须拿礼物,如果该 ...
随机推荐
- Java集合类面试题
java.util包中包含了一系列重要的集合类,而对于集合类,主要需要掌握的就是它的内部结构,以及遍历集合的迭代模式. 1.Java集合框架是什么?说出一些集合框架的优点? 每种编程语言中都有集合,最 ...
- jmeter添加自定义扩展函数之图片base64编码
打开eclipse,新建maven工程,在pom中引入jmeter核心jar包: <!-- https://mvnrepository.com/artifact/org.apache.jmete ...
- 虚拟现实-VR-UE4-构建光照显示光照构建失败,Swarm启动失败
闲的无聊折腾,发现想构建光照的时候,总是显示失败 如下图 百度许久,有大神指出是我在编译源码的的时候没有将其中的某个模块编译进去,只需要重新编译摸个模块就好 在UE4 的sln文件下,会看到一个Unr ...
- MySQL☞聚合函数/分组函数
分组函数(聚合函数) 1.count(*/列名): a.*:求出该数据的总条数 select count(*) from 表名 b.列名:求出该列中列名不为null的总条数 select cou ...
- Windows Server2003下安装IIS服务脑图
在练习过程中,勾选“ASP.NET”后开始安装时提示要插入安装光盘,但是我安装系统时是用镜像文件在虚拟机里安装的,所以根据提示界面的提示从文件中选择相应文件复制,如下图点击确定,选择iisapp.vb ...
- cocos2d-x 动作类
动作类是Action IntervalAction是间隔动作,InstantAction是瞬时动作. 动作的管理是要由节点负责的,任何的节点都可以管理节点,如精灵.菜单.层.甚至场景都可以管理动作.节 ...
- #Spring实战第二章学习笔记————装配Bean
Spring实战第二章学习笔记----装配Bean 创建应用对象之间协作关系的行为通常称为装配(wiring).这也是依赖注入(DI)的本质. Spring配置的可选方案 当描述bean如何被装配时, ...
- python基础训练营04-函数
任务四 函数的关键字 函数的定义 函数参数与作用域 函数返回值 一.函数的关键字: def 二.函数的定义: 在Python中,定义一个函数要使用def语句,依次写出函数名.括号.括号中的参数和冒号 ...
- 修改freemarker的ftl时,不重启tomcat的办法(使用了springMVC)
一.在使用Freemarker 时,需要在spring-mvc.xml 配置文件中作如下配置: <!-- 配置freeMarker的模板路径 --> <bean id="f ...
- select2赋值需要注意
$('#mySelect2').val(data.id).trigger('change'); 需要在赋值后,调用下change事件,不然的话展示值的span不会显示select最新的选中值.