例题1
数字三角形

题目描述

观察下面的数字金字塔。

写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。

      7 3   8 8   1   0 2   7   4   4 4   5   2   6   5

在上面的样例中,从7 到 3 到 8 到 7 到 5 的路径产生了最大

输入格式

第一个行包含 R(1<= R<=1000) ,表示行的数目。

后面每行为这个数字金字塔特定行包含的整数。

所有的被供应的整数是非负的且不大于100。

输出格式

单独的一行,包含那个可能得到的最大的和。

样例数据

input

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

output

30

方法一:搜索

路径起点终点明确,走法固定,可以考虑搜索解决。

定义递归函数void  dfs(int x,int y,int curr),表示从(1,1)走到点(x,y)的权值和curr。

当x=n时,到达边界。用所得的curr和ans比较,如果curr比较大,更新ans的值。

如果x<n,继续递归,dfs(x+1,y,curr+a[x+1][y])和dfs(x+1,y+1,a[x+1][y+1])。

参考程序:
#include<bits/stdc++.h>
using namespace std;
int a[1010][1010],ans=0;
int n;//输入数塔层数n
void dfs(int x,int y,int curr)
{
    if(x==n)
    {
        if(curr>ans) ans=curr;
        return ;
    }
    dfs(x+1,y,curr+a[x+1][y]);
    dfs(x+1,y+1,curr+a[x+1][y+1]); 
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            cin>>a[i][j];
    dfs(1,1,a[1][1]); 
    cout<<ans<<endl; 
    return 0;
}
此方法实际把所有的路径都走了一遍,由于每条路径有n-1步组成,每一步有左下,右下两种选择,路径总数为2n-1   时间复杂度为O(2n-1),

方法二:记忆化搜索

方法一有严重的问题,当n比较大时会花费太多时间,因为它进行了重复的搜索。如一条路径为13-11-12-6-12,另一条为13-11-12-6-7,对于11,12,6这些点进行了重复的搜索。我们完全可以在第一次搜索这些点时,就将这些点到终点的最大权值和记录下来,当我们再次到达这个点,就可以直接调用,不用重复搜索,这种方法就叫记忆化搜索。

因此,我们需要重新定义递归函数dfs。定义dfs(int x,int y)表示(x,y)到终点的最大权值和,最后的答案就是dfs(1,1).

为了避免重复搜索,我们开设一个数组dg[x][y]表示(x,y)点到终点的最大权值。

参考程序:

#include<bits/stdc++.h>
using namespace std;
int a[550][550],dg[550][550];
int n;
int dfs(int x,int y)
{
    if(dg[x][y]==-1)//判断是否已经计算过 
    {
        if(x==n)  dg[x][y]=a[x][y];
        else dg[x][y]=max(dfs(x+1,y),dfs(x+1,y+1))+a[x][y];
    }
    return dg[x][y];//计算过直接返回 
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
        {
            cin>>a[i][j];
            dg[i][j]=-1;
        }    
    dfs(1,1);
    cout<<dg[1][1]<<endl;
    return 0;
}

由于每一个点都只计算过一次,且是O(1)的时间,所以这种方法的时间复杂度为O(N*N)(n的平方)

方法三:动态规划(顺推法)

方法二的记忆化搜索,从本质来讲已经算是动态规划了。下面从动态规划的角度分析解题过程。

1.确定状态:

题目求解(1,1)到最底层路径的最大权值,路径起点固定,终点和中间点不确定,因此定义dg[x][y]表示从(1,1)出发到(x,y)路径的最大权值和。最终答案就是寻找最底层的最大值,ans=max{dg[n][1],dg[n][2],dg[n][3]...dg[n][n]}.

2.确定状态转移方程和边界条件

不去考虑(1,1)到(x,y)中间是怎么走的,只需要考虑(x,y)上一步是怎么来的,上一步可能是(x-1,y),也可能是(x-1,y-1),因此dg[x][y]的最大值就是上一步的最大权值和:max{dg[x-1][y],dg[x-1][y-1]),再加上自己的权值a[x][y]。

所以状态转移方程就是:dg[x][y]=max{dg[x-1][y],dg[x-1][y-1]}+a[i][j]。

与递归一样,我们也需要终止条件,防止无限递归下去。我们发现dg[x][y]的值取决于dh[x-1][y],dg[x-1][y-1],随着递归的深入,最后一定会递归到dg[1][1],dg[1][1]不能再使用状态转移方程了,所以给dg[1][1]附一个初值,即a[1][1]。

参考程序

#include<bits/stdc++.h>
using namespace std;
int a[550][550],dg[550][550];
int n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            cin>>a[i][j];
    dg[1][1]=a[1][1];    
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            dg[i][j]=max(dg[i-1][j-1],dg[i-1][j])+a[i][j];
        }
     } 
     int ans=0;
     for(int i=1;i<=n;i++)
         ans=max(ans,dg[n][i]);
    cout<<ans<<endl;
    return 0;
}

这种方法的时间复杂度一样是O(n*n)(n的平方)

方法四:动态规划(逆推)

从根节点,最底层的点出发,向上走,选择它上一层两个方向的最大值为最大的路径,然后从n-1层继续向上,找自己两个方向的最大值,一直到(1,1)。

动态规划求解过程就可以归纳为:自顶向下分析,自底向上计算。

下面具体分析整个过程,大家就明白了。

以问题描述中的样例为例。

第4层  6  14  15  8

6这个点要找最大路径,就要选择和它连接12和7中的最大值,同理14选择7和13中最大值,15选择13和24中最大值,8选择24和11中最大值,他们分别选择两个方向的最大值,然后第4层就更新为了

18  27  39  32

同理第三层12  7  26

寻找两个方向的最大值,不过两个方向的值已经更新,选择新的值中的最大值,12选择18和27,7选择27和39,26选择39和32

得到第三层为39  46  65

第二层 11  8

更新为57  73

最后第一层选择57和73,a[1][1]就为73+13=86,即最后的答案。

参考程序:

#include<bits/stdc++.h>
using namespace std;
int a[550][550];
int n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            cin>>a[i][j];    
    for(int i=n-1;i>=1;i--)
    {
        for(int j=1;j<=i;j++)
            a[i][j]+=max(a[i+1][j],a[i+1][j+1]);//寻找最大的路径 
     } 
    cout<<a[1][1]<<endl;
    return 0;
}

递推方程:

f[i][j]=max(f[i+1][j],f[i+1][j+1])+a[i][j];

或f[i][j]=max(f[i-1][j],f[i-1][j-1])+a[i][j];

没有本质上的区别 ,但最好从上往下推,可以有效防止边界出问题;

例题2

摘花生

题目描述

Hello Kitty想摘点花生送给她喜欢的米老鼠。

她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。

地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。

Hello Kitty只能向东或向南走,不能向西或向北走。

问Hello Kitty最多能够摘到多少颗花生。

输入格式

第一行是一个整数T,代表一共有多少组数据。

接下来是T组数据。

每组数据的第一行是两个整数,分别代表花生苗的行数R和列数 C。

每组数据的接下来R行数据,从北向南依次描述每行花生苗的情况。每行数据有C个整数,按从西向东的顺序描述了该行每株花生苗上的花生数目M。

输出格式

对每组输入数据,输出一行,内容为Hello Kitty能摘到得最多的花生颗数。

样例

输入样例

2
2 2
1 1
3 4
2 3
2 3 4
1 6 5

输出样例

8
16

代码如下:

#include<bits/stdc++.h>
using namespace std;
int a[110][110];
int dp[110][110];
int t;
int main()
{freopen("peanut.in","r",stdin);freopen("peanut.out","w",stdout);cin>>t;for(int k=1;k<=t;k++){memset(a,0,sizeof(a));memset(dp,0,sizeof(dp));int x,y;cin>>x>>y;for(int i=1;i<=x;i++){for(int j=1;j<=y;j++){cin>>a[i][j];}}dp[x][y]=a[x][y];for(int i=x;i>=1;i--){for(int j=y;j>=1;j--){dp[i][j]=max(dp[i+1][j],dp[i][j+1])+a[i][j];}}cout<<dp[1][1]<<endl;}return 0;
}

和数字三角形没有太大区别,主要体现的感觉是后无效性;

由于它只可以往东面或者往南面走,所以之和上面的和左面的有关;

递推方程:

dp[i][j]=max(dp[i+1][j],dp[i][j+1])+a[i][j];

题目3

公交乘车

题目描述

一个特别的单行街道在每公里处有一个汽车站。顾客根据他们乘坐汽车的公里使来付费。例如下表就是一个费用的单子。

公里数 1 2 3 4 5 6 7 8 9 10
价格 12 21 31 40 49 58 69 79 90 101

没有一辆车子行驶超过10公里,一个顾客打算行驶n公里()1<=n<=100),它可以通过无限次的换车来完成旅程。最后要求费用最少。

输入格式

第一行十个整数分别表示行走1到10公里的费用(<=500)。注意这些数并无实际的经济意义,即行驶10公里费用可能比行驶一公里少。

第二行一个整数n表示,旅客的总路程数。

输出格式

仅一个整数表示最少费用。

样例数据

input

12 21 31 40 49 58 69 79 90 101
15

output

147

代码如下

#include<bits/stdc++.h>
using namespace std;
int x;
int a[110];
int dp[110];
int main()
{freopen("buses.in","r",stdin);freopen("buses.out","w",stdout);for(int i=1;i<=10;i++){cin>>a[i];}memset(dp,10,sizeof(dp));cin>>x;dp[0]=0;dp[1]=a[1];for(int i=2;i<=x;i++){for(int j=1;j<=10&&j<=i;j++){dp[i]=min(dp[i],dp[i-j]+a[j]);}}cout<<dp[x];return 0;
}

注意一点就是提上数据也要更新

递推思想就是要知道i-1,i-2......i-10所花费的钱;

递推方程

f[i]=min(f[i-k]+cost[k],f[i])

题目4

黑熊过河

题目描述

晶晶的爸爸给晶晶出了一道难题:有一只黑熊想过河,但河很宽,黑熊不会游泳,只能借助河面上的石墩跳过去。它可以一次跳一墩,也可以一次跳两墩,但是每跳一次都会耗费一定的能量,黑熊最终可能因能量不够而掉入水中。所幸的是,有些石墩上放了一些食物,些食物可以给黑熊增加一定的能量。问黑熊能否利用这些石墩安全地抵达对岸?请计算出抵达对岸后剩余能量的最大值。

输入格式

第1行包含两个整数和P和Q(0≤P,Q≤1000),其中P表示黑熊的初始能量,Q表示黑熊每次起跳时耗费的能量。

第2行只有一个整数n(1≤n≤10^6),表示河中石墩的数目。

第3行有n个整数,表示每个石墩上食物的能量值ai(0≤ai≤1000)。

输出格式

输出1行,若黑熊能抵达对岸,输出抵达对岸后剩余能量的最大值;若不能,则输出“NO”。

样例数据

input

12 5
5
0 5 2 0 7

output

6

代码如下:

#include<bits/stdc++.h>
using namespace std;
int n,q,p,num,a[1000005],f[1000005];
int main()
{freopen("river.in","r",stdin);freopen("river.out","w",stdout);cin>>p>>q>>n;for(int i=1;i<=n;i++){cin>>a[i];}f[0]=p;for(int i=1;i<=n+1;i++){num=max(f[i-1],f[i-2]);if(num>=q){f[i]=num-q+a[i];}   else {cout<<"NO"<<endl;   return 0;}}cout<<f[n+1]<<endl;   return 0;
}

这道题在前面的基础上有所提升,但注意本质一样;

理解上图我们便可以从它的i-1和i-2个来算,熊可以从a[i-1]和a[i-2]个方向,因此在形成即可。

dp[i]=max(dp[i],dp[i-2]-q+a[i]);

在加一个判断num>q是就噶了,最直接结束就行;

题目5

lis模板题

题目描述

有N个整数,输出这N个整数的最长上升序列、最长下降序列、最长不上升序列和最长不下降序列。

输入格式

第一行,仅有一个数N。 N<=700000 第二行,有N个整数。 每个数−109<=每个数<=109

输出格式

第一行,输出最长上升序列长度。

第二行,输出最长下降序列长度。

第三行,输出最长不上升序列长度。

第四行,输出最长不下降序列长度。

样例数据

input

10
1 3 0 8 6 2 3 1 4 2

output

4
4
4
4

这是后面几道题的基础,4种序列对应4个求法;

但是由于数据太大,o(n^2)的做法会超时,如下:

for(int i=2;i<=n;i++){for(int j=1;j<i;j++){if(a[j]<a[i]) {dp1[i]=max(dp1[i],dp1[j]+1); }}}

所以在比较大小的时候,不再采取一个一个枚举的方法,直接采用stl的二分查找

代码如下:

#include <bits/stdc++.h>
using namespace std;
int n,a[700100],dp[700100];
void work1()
{memset(dp,10,sizeof(dp));dp[1]=a[1];for(int i=2;i<=n;i++){*lower_bound(dp+1,dp+2+n,a[i])=a[i];} cout<<lower_bound(dp+1,dp+2+n,dp[0])-dp-1<<endl;
}
void work2()
{memset(dp,10,sizeof(dp));dp[1]=a[n];for(int i=n-1;i>=1;i--){*lower_bound(dp+1,dp+2+n,a[i])=a[i];}cout<<lower_bound(dp+1,dp+2+n,dp[0])-dp-1<<endl;
}
void work3()
{memset(dp,10,sizeof(dp));dp[1]=a[n];for(int i=n-1;i>=1;i--){*upper_bound(dp+1,dp+2+n,a[i])=a[i];}cout<<lower_bound(dp+1,dp+1+n,dp[0])-dp-1<<endl;
}
void work4()
{memset(dp,10,sizeof(dp));dp[1]=a[1];for(int i=2;i<=n;i++){*upper_bound(dp+1,dp+2+n,a[i])=a[i];} cout<<lower_bound(dp+1,dp+2+n,dp[0])-dp-1<<endl;
}
int main()
{freopen("data.in","r",stdin);freopen("data.out","w",stdout);scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&a[i]);work1();work2();work3();work4();
return 0;
}

题目6

导弹拦截

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。   

输入导弹的枚数和导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数,每个数据之间有一个空格),计算这套系统最多能拦截多少导弹?

如果要拦截所有导弹最少要配备多少套这种导弹拦截系统?

输入格式

第一行数字n表示n个导弹(n<=200) 第二行n个数字,表示n个导弹的高度

输出格式

一个整数,表示最多能拦截的导弹数

一个整数,表示要拦截所有导弹最少要配备的系统数

样例数据

input

8
389  207  155  300  299  170  158  65

output

6
2

本题中的导弹拦截系统所对应的就是一个最长不上升子序列,

第一问输出长度就可以了;

但是第二问就有点ex了,他不能以用第一问的算法,一直寻找得到的结果是有返例的;

eg     7  5  4  1  6  3  2;

有3个不上升序列,但实际需要两个就可以解决;

正确的思路是从该导弹的所选系统入手来解决,具体分两种;

1.当前系统无法拦截,那必须要新建了;

2.当前系统可拦截,但到底要不要呢;

这时候就可以借助一个比较抽象的东西————潜力;

也就是这个导弹如果作为最高标准了,能够拦下的导弹数是多少;

得到每个导弹的潜力以后,就可以当要进入系统的时候进行比较,

把潜力值较小的选进去,这样潜力值大的就可以作为标准篮下更多的导弹;

;;;————确实很难想到,该方法的代码如下;

void work2()
{sys[++ans2]=a[1];for(int i=2;i<=n;i++){int minn=1e9;int u;for(int j=1;j<=ans2;j++){if(sys[j]>=a[i]&&sys[j]<minn){minn=sys[j];u=j;}}if(minn!=1e9) sys[u]=a[i];else{sys[++ans2]=a[i];}}
}

实在无语的我想去查一下有没有简单方法,让就去查了查,发现了

最小链覆盖——Dilworth定理

狄尔沃斯定理(Dilworth’s theorem)是关于偏序集的极大极小的定理.该定理断言:对于任意有限偏序集,其最大反链中元素的数目必等于最小链划分中链的数目.

虽然没学过,但是这句话在本题中就是说

可以简易理解为:(反链应该就是最长上升序列了)

要求最少的覆盖,按照Dilworth定理最少链划分 = 最长反链长度所以最少系统 = 最长导弹高度上升序列长度。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int a[700700],dp[700700];
int n;
void work3()
{memset(dp,10,sizeof(dp));dp[1]=a[n];for(int i=n-1;i>=1;i--){*upper_bound(dp+1,dp+2+n,a[i])=a[i];}cout<<lower_bound(dp+1,dp+1+n,dp[0])-dp-1<<endl;
}
void work1()
{memset(dp,10,sizeof(dp));dp[1]=a[1];for(int i=2;i<=n;i++){*lower_bound(dp+1,dp+2+n,a[i])=a[i];} cout<<lower_bound(dp+1,dp+2+n,dp[0])-dp-1<<endl;
}
int main()
{freopen("missile.in","r",stdin);freopen("missile.out","w",stdout);cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}work3();work1();return 0;

题目7

合唱队形

题目描述

  N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。

  合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<...Ti+1>…>TK(1<=i<=K)。

  你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入格式

输入的第一行是一个整数N(2< =N< =100),表示同学的总数。

第一行有n个整数,用空格分隔,第i个整数Ti(130<=Ti<=230)是第i位同学的身高(厘米)。

输出格式

输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。

样例数据

input

8
186 186 150 200 160 130 197 220

output

4

题意也很清晰了,一个 上升一个下降,求和之后再做个差就知道踢掉多少人了

#include<bits/stdc++.h>
using namespace std;
int n,a[110];
int dp1[110],dp2[110];
int ans[110],maxx=0;
int main()
{freopen("chorus.in","r",stdin);freopen("chorus.out","w",stdout);cin>>n;for(int i=1;i<=n;i++){cin>>a[i];} for(int i=1;i<=n;i++){dp1[i]=1;dp2[i]=1;}for(int i=2;i<=n;i++){for(int j=1;j<i;j++){if(a[j]<a[i]) {dp1[i]=max(dp1[i],dp1[j]+1); }}}for(int i=n-1;i>=1;i--){for(int j=n;j>i;j--){if(a[j]<a[i]){dp2[i]=max(dp2[i],dp2[j]+1);} } }for(int i=1;i<=n;i++){ans[i]=dp1[i]+dp2[i];maxx=max(maxx,ans[i]);}cout<<n-maxx+1;return 0;
}

题目8

轮船问题

题目描述

某国家被一条河划分为南北两部分,在南岸和北岸总共有N对城市,每一城市在对岸都有一个城市作为友好城市。每一对友好城市都希望有一条航线来往,于是他们向政府提出了申请。

由于河终年有雾。政府决定允许开通的航线就互不交叉(如果两条航线交叉,将有很大机会撞船)。兴建哪些航线以使在安全条件下有最多航线可以被开通。

输入格式

  第一行两个由空格分隔的整数x,y,10<=x,y<=60000,x,y中较长的表示河的长度另一个表示宽。

第二行是一个整数N(1<=N<=5000),表示分布在河两岸的城市对数。接下来的N行每行有两个由空格分隔的正数C,D (、(C、D<=x),描述每一对友好城市与河起点的距离,C表示北岸城市的距离而D表示南岸城市的距离。在河的左边,任何两个城市的位置都是不同的。

输出格式

一个整数,表示安全条件下能够开通的最大航线数目

样例数据

input

30  4
5
4  5
2  4
5  2
1  3
3  1

output

3

实际应用吧,画图理解一下就可以发现其实还是一最长上升序列

长和宽就是搞笑的,sort一下x之后求y就行了;

代码如下

#include<bits/stdc++.h>
using namespace std;
struct node
{int x,y;
}a[5500];
int xx,yy,n;
int dp[5500];
bool mysort(node xx,node yy)
{return xx.x<yy.x ||(xx.x==yy.x&&xx.y <yy.y);
}
int main()
{freopen("ship.in","r",stdin);freopen("ship.out","w",stdout);cin>>xx>>yy>>n;for(int i=1;i<=n;i++){cin>>a[i].x>>a[i].y;}sort(a+1,a+1+n,mysort);memset(dp,10,sizeof(dp));dp[1]=a[1].y;for(int i=2;i<=n;i++){*lower_bound(dp+1,dp+2+n,a[i].y)=a[i].y ;} cout<<lower_bound(dp+1,dp+2+n,dp[0])-dp-1<<endl;return 0;
}

题目9

Farmer_John收苹果

题目背景

FJ是我的小呀小苹果,怎么爱你都不嫌多 红红的小苹果温暖我的心窝,点亮我心中的火~火火火火~~~~                        —— Bessie

题目描述

农场的夏季是收获的好季节。在Farmer John的农场,他们用一种特别的方式来收小苹果:Bessie摇小苹果树,小苹果落下,然后Farmer John尽力接到尽可能多的小苹果。

作为一个有经验的农夫,Farmer John将这个过程坐标化。他清楚地知道什么时候(1<=t<=1,000,000)什么位置(用二维坐标表示,−1000<=x,y<=1000)会有小苹果落下。他只有提前到达那个位置,才能接到那个位置掉下的小苹果。

一个单位时间,Farmer John能走s (1<=s<=1000)个单位。假设他开始时(t=0)站在(0,0)点,他最多能接到多少个小苹果?

Farmer John 在接小苹果时,从某个点到另外一点按照直线来走。

输入格式

第一行:N(小苹果个数)和S(速度)

第2..N+1行:每行三个数Xi,Yi,Ti,表示每个小苹果掉下的位置和落下的时间。

输出格式

仅一行,一个数,表示最多能接到几个小苹果

样例数据

input

5 3
0 0 1
0 3 2
-5 12 6
-1 0 3
-1 1 2

output

3

苹果属性有位置和时间,肯定要紧着近的捡;

而且要拿两个的话,注意时间要够用;

用f[i]表示第i个苹果拿,最多拿几个;

所以

f[i]=max(f[j])+1;         j<=i;

注意2点

1》两点间距离公式小心爆int,用double型;

2》赋初值的时候,要赋成-1,防止不可能的情况;

代码如下:

#include<bits/stdc++.h>
using namespace std;
int n,s;
int dp[5005];
struct node1{int x,y,t;
}a[5005];
int res;
bool mycmp(node1 w1,node1 w2) {return w1.t<w2.t;}
double gougu(int i,int j){double temp;temp=sqrt(1.0*(a[i].x-a[j].x)*(a[i].x-a[j].x)+1.0*(a[i].y-a[j].y)*(a[i].y-a[j].y));return temp;
}
int main(){freopen("apple.in","r",stdin);freopen("apple.out","w",stdout);cin>>n>>s;for(int i=1;i<=n;i++){cin>>a[i].x>>a[i].y>>a[i].t;}sort(a+1,a+1+n,mycmp);memset(dp,-1,sizeof(dp));res=0;dp[0]=0;for(int i=1;i<=n;i++){for(int j=0;j<i;j++){if(dp[j]>=0&&((a[i].t-a[j].t)*s>=gougu(i,j)))dp[i]=max(dp[i],dp[j]+1);}res=max(res,dp[i]);}printf("%d\n",res);return 0;
}

题目10

护卫队

题目描述

护卫车队在一条单行的街道前排成一队,前面河上是一座单行的桥。因为街道是一条单行道,所以任何车辆都不能超车。桥能承受一个给定的最大承载量。

为了控制桥上的交通,桥两边各站一个指挥员。护卫车队被分成几个组,每组中的车辆都能同时通过该桥。当一组车队到达了桥的另一端,该端的指挥员就用电话通知另一端的指挥员,这样下一组车队才能开始通过该桥。

每辆车的重量是已知的。任何一组车队的重量之和不能超过桥的最大承重量。被分在同一组的每一辆车都以其最快的速度通过该桥。一组车队通过该桥的时间是用该车队中速度最慢的车通过该桥所需的时间来表示的。

问题要求计算出全部护卫车队通过该桥所需的最短时间值。

输入格式

输入文件第一行包含三个正整数(用空格隔开),第一个整数表示该桥所能承受的最大载重量(用吨表示); 第二个整数表示该桥的长度(用千米表示); 第三个整数表示该护卫队中车辆的总数(n<1000)

接下来的几行中,每行包含两个正整数W和S(用空格隔开),W表示该车的重量(用吨表示),S表示该车过桥能达到的最快速度(用千米/小时表示)。 车子的重量和速度是按车子排队等候时的顺序给出的。

输出格式

输出文件应该是一个实数,四舍五入精确到小数点后1位,表示整个护卫车队通过该桥所需的最短时间(用分钟表示)。

样例数据

input

100 5 10
40 25
50 20
50 20
70 10
12 50
9 70
49 30
38 25
27 50
19 70

output

75.0

按照动态规划的思想方法,我们:

(1)设 f[i] 为前i辆车通过桥的最短时间,设 t[i][j] 为第i-第j辆车(租车的车队)通过所需时间;

(2)分析方程:

综合起来,我们就得到了方程

但是也不要忘记了条件

(3)其实我们也可以这样理解,对于每一辆车i,j从第i-1辆车开始倒推,

看看总重是否小于最大载重量;是则能组成车队,

否则不能,且前面的j-1辆车不能与i组成车队;

【细节处理】

(1)预处理重量

定义W[i]为前i辆车的总重 (即前缀和,是处理这类问题非常好的方法,可以降低时间复杂度)

需要知道第j辆车到第i辆车的总重时,用W[i]-W[j-1]就可以了;

(2)预处理时间

(值得一提的是本题解直接将时间计算出来,转为分钟,方便之后程序编写)

方法是:

定义Sb为桥的全长;

v[i]为第i辆车的速度;

T[i]:第i辆车通过桥所需时间;

t[i][j]:第i辆车到第j辆车组成的车队通过桥所需的时间;

(3)w数组、v数组和W数组一定要开long long,否则两个较大的int相加会炸掉!

#include<bits/stdc++.h>
using namespace std;
long long Wb,Sb,n,w[1000],v[1000],W[1000];
double T[1000],t[1000][1000],f[1000];
/*Wb:the weigh of the bridge;Sb:the distance of the bridge;w[i]:the weigh of car i; v[i]:the speed of car i;T[i]:the time for car i to go across the bridge; (minutes)W[i][j]:the sum of w[i],w[i+1],...,w[j];t[i][j]:the time for car i,car i+1,...,car j to go across the bridge; (minutes)f[i]:we must spend f[i] to let car 1,car 2,...,car i to go;一定要开long long,否则会炸!
*/int main()
{freopen("guard.in","r",stdin);freopen("guard.out","w",stdout);cin>>Wb>>Sb>>n;for(int i=1;i<=n;i++){cin>>w[i]>>v[i];   T[i]=(double)Sb/v[i]*60;t[i][i]=T[i];}for(int i=1;i<=n-1;i++) {for(int j=i+1;j<=n;j++){t[i][j]=max(t[i][j-1],T[j]);}}for(int i=1;i<=n;i++){W[i]=W[i-1]+w[i];}    for(int i=1;i<=n;i++){f[i]=T[i]+f[i-1];  //前面i-1辆车所花的时间,加上第i辆车所花时间,就是前i辆车所花时间 for(int j=i-1;j>=1;j--)   //倒回去查,看看能不能组成一个从j到i的车队 {if (W[i]-W[j-1]<=Wb) {f[i]=min(f[i],f[j-1]+t[j][i]);  //cout<<f[i]<<" "; //能组成车队,比较时间 }else break;  //不能组成车队 }}printf("%0.1lf",f[n]); return 0;
} 

难死了呜呜呜。。。

dp题目总结(1)——基础相关推荐

  1. UVA dp题目汇总

    UVa专题练习 A-4 10003 经典dp,可用四边形不等式优化 10029 基础dp,DAG最长路,需高效构图 10032 经典问题.子集和数问题.01背包问题 10036 能否在一个整数序列的每 ...

  2. 合肥对口计算机,2021年合肥市对口考试要做哪些题目?计算机应用基础(Windows7+office2010)周测月考单元卷...

    &nbsp&nbsp[导读]:2021年合肥市对口考试要做哪些题目?计算机应用基础(Windows7+office2010)周测月考单元卷,更多安徽对口考试报名时间.考试时间以及考试模 ...

  3. 题目1452:搬寝室(dp题目)

    题目链接:http://ac.jobdu.com/problem.php?pid=1452 详解链接:https://github.com/zpfbuaa/JobduInCPlusPlus 参考代码: ...

  4. CROC-MBTU 2012, Elimination Round (ACM-ICPC) H DP题目

    http://codeforces.com/contest/245/problem/H 题意: 给定一个字符串s (1 ≤ |s| ≤ 5000) 然后又q个询问(1 ≤ q ≤ 106)  每次询问 ...

  5. 插头DP题目泛做(为了对应WYD的课件)

    题目1:BZOJ 1814 URAL 1519 Formula 1 题目大意:给定一个N*M的棋盘,上面有障碍格子.求一个经过所有非障碍格子形成的回路的数量. 插头DP入门题.记录连通分量. 1 #i ...

  6. 这段时间做的简单dp题目(部分)

    这些时间vj上做的部分题目 HDU5115 题意:第一行t,t组测试数据,每组数据第一行输入n表示n匹狼,第二行给出一个序列表示每匹狼的伤害,第三行给出每匹狼能给周围狼的伤害增幅,求怎样打可以得到最小 ...

  7. 各公司 Java 面试题目整理(基础+高级+算法+数据库)

    包含 Java 面试的各个方面,史上最全,苦心整理最全 Java 各公司面试题目整理包括但不限于基础+高级+算法+数据库优化+算法优化,使用层面广,知识量大,涉及你的知识盲点.要想在面试者中出类拔萃就 ...

  8. 大学计算机基础论文范文大全集,计算机基础论文题目集 计算机基础毕业论文题目怎样定...

    汇总了[100道]与计算机基础相关论文选题,为广大毕业生和职称者推荐计算机基础论文题目集,解决在校大学生不知道计算机基础毕业论文题目怎样定等相关问题! 一.比较好写的计算机基础论文题目: 1.大学计算 ...

  9. 51nod 1201:整数划分 超级好的DP题目

    1201 整数划分 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题  收藏  关注 将N分为若干个不同整数的和,有多少种不同的划分方式,例如:n = 6,{6} { ...

  10. 树形DP题目。。。转载

    树形DP http://acm.hdu.edu.cn/showproblem.php?pid=2196√ 向下搜一遍,向上搜一遍 http://acm.hdu.edu.cn/showproblem.p ...

最新文章

  1. try catch finally 关闭流标准的写法
  2. linux 测试vim编译器_软件测试工程师必须要掌握的linux命令
  3. gsonformat插件_裂墙推荐!IntelliJ IDEA 常用插件一览,让效率成为习惯
  4. 快速浏览Silverlight3 beta:鸡肋一样的WritableBitmap
  5. 鸢尾花数据集的数据可视化
  6. KEIL STC 仿真
  7. c# WPF中通过双击编辑DataGrid中Cell的示例(附源码)
  8. 求矩形和圆柱的体积(用到了接口)
  9. Excel 2010去掉网格线
  10. 163邮箱登录页面在哪儿?如何在手机、电脑上登陆163邮箱?
  11. 拼图·面部表情捕捉 | design-ai-lab
  12. Word设置默认隐藏页面间空白
  13. Glassfish的安装与使用
  14. linux硬盘检测健康状态脚本,系统运维|使用 smartmontools 查看硬盘的健康状态
  15. 数据分析-ARIMA方法建模步骤总结
  16. 示波器的带宽、带宽检定方法
  17. html数据线如何使用,揭秘数据线DIY详细步骤
  18. 怎样制作Lrc歌词文件
  19. 大数据时代的Serverless工作负载预测赛后总结
  20. python的开发者太负责任了_人生苦短,我用 Python

热门文章

  1. 如何使用Internet Download Manager批量下载音乐素材?
  2. STK入门级模拟卫星轨道
  3. ubuntu16.04 360随身WiFi2
  4. 今天美国大学计算机硕士放榜吗,美国大学研究生offer放榜时间一般是什么时候?别错过哟!...
  5. 远程桌面由于以下原因无法连接远程计算机,win10远程桌面提示由于以下原因之一无法连接的解决教程...
  6. performSelector延时调用导致的内存泄露
  7. eclipse没有web项目
  8. 每天进步一点点————MUMA架构优化和应用优化
  9. python自动化之控制浏览器
  10. SpringBoot+Vue项目的PDF导出及给PDF文件盖章的功能示例