贪心 ? OR DP?

来源:互联网 发布:winner2016淘宝造物节 编辑:程序博客网 时间:2024/06/07 03:51

守望者的逃离

Description

恶魔猎手尤迫安野心勃勃.他背叛了暗夜精灵,率深藏在海底的那加企图叛变:守望者在与尤迪安的交锋中遭遇了围杀.被困在一个荒芜的大岛上。为了杀死守望者,尤迪安开始对这个荒岛施咒,这座岛很快就会沉下去,到那时,刀上的所有人都会遇难:守望者的跑步速度,为17m/s, 以这样的速度是无法逃离荒岛的。庆幸的是守望者拥有闪烁法术,可在1s内移动60m,不过每次使用闪烁法术都会消耗魔法值10点。守望者的魔法值恢复的速度为4点/s,只有处在原地休息状态时才能恢复。
现在已知守望者的魔法初值M,他所在的初始位置与岛的出口之间的距离S,岛沉没的时间T。你的任务是写一个程序帮助守望者计算如何在最短的时间内逃离荒岛,若不能逃出,则输出守望者在剩下的时间内能走的最远距离。注意:守望者跑步、闪烁或休息活动均以秒(s)为单位。且每次活动的持续时间为整数秒。距离的单位为米(m)。

Input

仅一行,包括空格隔开的三个非负整数M,S,T。

Output

包含两行: 第1行为字符串"Yes"或"No" (区分大小写),即守望者是否能逃离荒岛。
第2行包含一个整数,第一行为"Yes" (区分大小写)时表示守望着逃离荒岛的最短时间
第一行为"No" (区分大小写) 时表示守望者能走的最远距离。

Sample Input

39 200 4

Sample Output

No197


这题是贪心吗?

一开始打算写两个方程 ,解一解,后面猛然醒悟:我这种数学水平去解方程式,这不是sb么。   然后就去老实想贪心了。

这个题目啊,用贪心的思维去想啊, 我觉得要写一大堆细节,wa个十几发 可能能写出来吧。   反正我是写不出来。 

下面是用贪心过的人的想法:

主体思想:先狂用闪烁,魔法不够了再根据时间、魔法值和距离来判断下一步是跑步还是恢复魔法值后闪烁。 

思考后得出了一个用光魔法后由时间、魔法、距离的关系所做的决策。


由上表格,就可以使用范围判断法来确定决策了。

解释:例如,当魔法值是 0 或 1 时,如要闪烁一次需恢复 3 秒,加上闪烁时间共 4秒,才跑 60m ,而跑步就可以跑 17 × 4=68m 了。因此,如在时间足够的前提下,恢复 5 秒闪烁 2 次,共花费 7 秒,跑了 60 × 2=120m ,而跑步只能跑 17 × 7=119m 。因此,在条件允许的情况下,所作决策为恢复 + 闪烁。至于距离剩余的条件,大家可以想一下,当前的剩余距离如果并不需要使用恢复 + 闪烁,而可以用更少的时间用跑步来完成,那当然用跑步好啦!当剩余距离不大于 102 时,跑步最多只需花 102 ÷17=6 (秒),而不需要再用 7 秒了。

魔法值剩余 2~9 的决策和条件原因同上。

此题用贪心需要考虑的一些点

1.  当开始狂用闪烁时如果魔法值还足够可是已经跑完了就会错误。

2.  同样,狂用闪烁时如果时间不够闪烁了也会错误。

3.  上面讲过了,剩余距离无需恢复 + 闪烁只需跑步的情况也要考虑到。

考虑到以上几点就可以开始写代码了 。


然后用贪心的思维去想想别的方法? 或者说用dp的思维来细细想想贪心?

我们不知道 到底是要 闪烁几次 + 跑步 几秒 + 休息几秒 才能够最快的跑出去,或者说规定时间跑的最远。

那用dp的想法就是我们都记录下来, 然后对于每一个状态我再来一个一个分析。

。。。。。然后我就看了大牛的很精妙的想法;

对比那个贪心的复杂想法,可以看看这个几行简单的代码,

const int N = 300000;int m,s,t,f[N];int main(){    scanf("%d%d%d",&m,&s,&t);    for(int i = 1;i <= t;i ++){        if(m >= 10){           f[i] = f[i - 1] + 60;           m -= 10;        }        else {            f[i] = f[i - 1];            m += 4;        }    }    for(int i = 1;i <= t;i ++){        if(f[i] < f[i - 1] + 17) f[i] = f[i - 1] + 17;        if(f[i] >= s){ printf("Yes\n%d",i);break; }        else if(i == t)printf("No\n%d",f[i]);    }    return 0;}


5.30加题: 经典题, 非常非常非常非常经典的题,  有很多人有很多理解, 无论从哪个方面去做这道题,去解释这道题 都是非常经典的,

1314: Pku3636 Nested Dolls

Description

Dilworth is the world's most prominent collector of Russian nested dolls: he literally has thousands of them! You know, the wooden hollow dolls of different sizes of which the smallest doll is contained in the second smallest, and this doll is in turn contained in the next one and so forth. One day he wonders if there is another way of nesting them so he will end up with fewer nested dolls? After all, that would make his collection even more magnificent! He unpacks each nested doll and measures the width and height of each contained doll. A doll with width w1 and height h1 will fit in another doll of width w2 and height h= if and only if w1 < w2 and h1 < h2. Can you help him calculate the smallest number of nested dolls possible to assemble from his massive list of measurements?
需要把一堆大小不同的玩具嵌套在一起,问最后最少剩几个玩具

Input

On the first line of input is a single positive integer 1 ≤ t ≤ 20 specifying the number of test cases to follow. Each test case begins with a positive integer 1 ≤ m ≤ 20000 on a line of itself telling the number of dolls in the test case. Next follow 2m positive integers w1, h1,w2, h2, ... ,wm, hm, where wi is the width and hi is the height of doll number i. 1 ≤ wi, hi ≤ 10000 for all i.

Output

For each test case there should be one line of output containing the minimum number of nested dolls possible.

Sample Input

4320 30 40 50 30 40420 30 10 10 30 20 40 50310 30 20 20 30 10410 10 20 30 40 50 39 51

Sample Output

1232

HINT

Dilworth定理


我自己的理解:

我一开始觉得是 贪心,后面觉得太难写了,转换着想了想dp 觉得更难。   这题其实以前大一的时候做过,但是当时没有吃透这些东西,所以根本记不了这么长的时间。

这是一个 偏序集,或者 贪心的思维?

首先 介绍一下我以前知道,但是并没有吃透而导致现在不太会,忘得差不多的这个知识点吧: 直接贴一个高玩的博客吧,写的非常好:

http://blog.csdn.net/xuzengqiang/article/details/7266034

有几个要点就是  反链 一定是任意两个都不可以比较,  链则是任意两个都可以比较,

重要的定理就是

定理1 令(X,≤)是一个有限偏序集,并令r是其最大链的大小。则X可以被划分成r个但不能再少的反链。 
其对偶定理称为
Dilworth定理:
定理2 令(X,≤)是一个有限偏序集,并令m是反链的最大(长)的大小(长度)。则X可以被划分成m个但不能再少的链。

这个定理也可以反着用啊,如果可以求出来  x可以最少被划分为m 个链,则最长的反链也就是m


了解完这些知识那么问题来了,题意显然是求最少可以分为多少个链??    那么 我们如何去找这个最长的反链呢?

我们做法如是:

先把所有的盒子 按w 递增,若 w相等则按h递减  ,为什么一定要按这个规则呢,先别急,后面我再慢慢讲解。

之后我们 在假设出一个反链, 并用数组h 保存其 h。

然后对于 一个新的 盒子  x  有 x.w 和x.h ,那么我们朴素的想法就是 对于反链中的所有元素 都与x 比较一遍,若存在一个 盒子j 可以放进盒子x 里面,则我们用盒子x 替换掉j 。

这样对所有的盒子扫一遍,那我们就可以得到一条最长的反链,  因为 如果这条链不是最长的,那么我们就把缺少的盒子加进来就好了。


第一次我是这样写的,但是时间复杂度显然接近  n^2;

再仔细思考下不难发现,其实我们按 w递增  ,h递减 排列那么 我们得到的反链 的高度 也就是h数组保存的值就是  递减的,   

w1    w2           若 w2>w1                                                                                                                                                     

h1     h2          很显然h2肯定会小于h1,否则 我们就可以把盒子1放进盒子2的里面了啊。


那么我们放进盒子i 的时候 就可以二分找到第一个  hj <  盒子i . h    !!!! 这里我发现降序排列的lower_bound 是无法使用的,感觉百度百科好几把坑。


忘记说为什么一定要按 w 相同的  h递减了,其实按上面的解释大家也许能够了解一点,但是我直接说一组数据能够更加了解透彻:

为什么要按 w 递增,h递减呢,因为我们要贪 啊

这一组数据:

5

1 8

2 3

2 4

3 5

4 4

形成反链:

开始放入,如果按h递增

1 -> 2  ->2

8     3     4 

然后3 5就替代反链中的2 3

1 -> 3 -> 2

8     5      4

这样是不是没有得到最优的反链,是吧因为我们应该替代2 4 留下2 3,形成的反链才会最短,得到的链也就最少啊。

果然对于4 4

我们就只能添加到反链中    对应的链

1 -> 3 -> 2 ->4           1   2->3   2   4

8     5      4    4            8   3    5   4   4

而存在一条更短的反链:    对应的链

1 -> 3 -> 4               1   2->3   2->4

8     5      4               8   4    5   3    4

 

意思就是说

我们必须在w相等的时候 把h 较大的先放进反链中,这样可以让h较大的先被覆盖,留下h较小的等待被覆盖



然后对于poj1065 这个题我也是醉了, 这个是在  w1=w2  h1=h2 的状态下   盒子2也可以覆盖盒子1 ,所以这种情况下肯定我们的排序就是  w递增  h也是递增啊,因为我们先替换小的之后再可以用这个大的来替换掉这个小的啊

#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<stdlib.h>#include<queue>#include<stack>#include<map>#include<vector>#define mem(a) memset(a,0,sizeof(a))#define INF 0x7fffffff   //INT_MAX#define inf 0x3f3f3f3f   //const double PI = acos(-1.0);const double e = exp(1.0);template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }bool cmpbig(int a,int b){return a>b;}bool cmpsmall(int a,int b){return a<b;}using namespace std;const int N = 20005;int m,s,t,w[N],h[N];struct node{int w,h;}f[N],ff[N];bool cmp(node a,node b){if(a.w==b.w)   return a.h>b.h;   //这里改成 >= 就死死的超时,但是我没搞明白为什么???elsereturn a.w<b.w;}int main(){int t;freopen("1.txt","r",stdin);    scanf("%d",&t);    while(t--){        int n;        scanf("%d",&n);    for(int i=1;i<=n;i++)    scanf("%d %d",&f[i].w,&f[i].h);    sort(f+1,f+n+1,cmp);    int len=0;    for(int i=1;i<=n;i++){    int flag=0;    int num=f[i].h,l=0,r=len-1;    while(l<r){    int mid=(l+r)/2;    if(h[mid]>=num)    l=mid+1;    else    r=mid-1;    }      for(int j=l;j<len;j++)    if(f[i].w>ff[j].w && f[i].h>ff[j].h){    flag=1;    ff[j]=f[i];    h[j]=ff[j].h;    break;    }    if(!flag){    h[len]=f[i].h;    ff[len++]=f[i];    }    }    printf("%d\n",len);    }    return 0;}
然后对于这道拦截导弹的题目:http://begin.lydsy.com/JudgeOnline/problem.php?id=1312显然也是需要找到一个最长的反链, 每个拦截系统只有一个第一发导弹的高度那我们显然是不可以排序的,排序就特么搞笑了,QwQ ,我们直接对每一发导弹,按顺序在反链中找 是否存在 可覆盖他的系统,若不存在,则加到反链最后即可;思考一下 :为什么不用去找那个此时高度最小的系统呢?因为 此时的反链中的系统高度必然是递增的:比如 导弹攻击的高度为  H1,H2,H3.....反链中:  第一个高度为H1, 若 H2<H1 ,则 我们就会沿用第一个导弹系统, 若H3>H2,那么我们就把H3放进反链之中 (即新增一个导弹系统)如果 对于 Hi  ,我们用H5 覆盖Hi  ,之后的高度<H4  ,这种情况实际上是不可能的,因为如果Hi < H4 ,那么我们直接用H4 覆盖它就好了啊综上:   这个反链的高度一定是递增的。。。。。。。
#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<stdlib.h>#include<queue>#include<stack>#include<map>#include<vector>#define mem(a) memset(a,0,sizeof(a))#define INF 0x7fffffff   //INT_MAX#define inf 0x3f3f3f3f   //const double PI = acos(-1.0);const double e = exp(1.0);template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }bool cmpbig(int a,int b){return a>b;}bool cmpsmall(int a,int b){return a<b;}using namespace std;const int N = 20005;int f[N],ff[N];int h[N];int main(){int t,len=1;freopen("1.txt","r",stdin);    while(~scanf("%d",&f[len++]));    int L=0;    for(int i=1;i<len;i++){    int flag=0;    //如果有需要就可以加二分    for(int j=0;j<L;j++){    if(f[i]<=ff[j]){    flag=1;    ff[j]=f[i];    break;    }    }    if(!flag){    ff[L++]=f[i];    }    }    printf("%d\n",L);    return 0;}




                                                                                                                                                                             

0 0