DP

DP 开宝箱(简单DP)
题目:
你确定我是来开宝箱不是来比赛的吗??艾玛,这是哪,天啊,伦家竟然穿越了。
摆在眼前的是1个n*m(0< n<=100&&0< m<=100)的宝箱矩阵,而你就在这个矩阵的左上角也就是入口处。冲啊,开宝箱去,然而脚不听话啊,你的脚失去了向左走与向上走(方向是相对于整个地图,换句话说不是通过你脸的朝向来决定方向,你只能↓和→)的能力。(你:这设定真奇怪啊 。WGX大神:让你四处走还不把我的宝箱全开了)。

不管了开宝箱要紧。假设每个宝箱有一个数字V,如果是正数那么你获得V个金币,如果是负数,哈哈,不好意思,扣去相应个金币。(哭晕在厕所)。
那么问题来了:

如果你在已知宝箱的奖励的情况下,请选择一条路线,这条路线能让你获得最多的金币,当然所有走过的宝箱必须打开,不能说因为是负数你就选择不去打开它,并且出口(左上)和入口(右下)的那两个宝箱是必经点,也必须打开。
你的任务只是求出该路线获得的金币值就行。

题解:
很明显状态转移方程为 f[i][j]=max(f[i-1][j],f[i][j-1])+b[i][j];
记得要DP数组初值赋值为无穷小

代码:

#include<stdio.h>
#include<limits.h>
int b[101][101];
int f[101][101];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m,i,j,k,kk;
        scanf("%d%d",&n,&m);
        for(i=0;i<=n+1;i++)
        {
            for(j=0;j<=m+1;j++)
                f[i][j]=INT_MIN;
        }
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
                scanf("%d",&b[i][j]);
        }
           for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
            {
                if(i==1 && j==1)
                    f[1][1]=b[1][1];
                else
                 f[i][j]=max(f[i-1][j],f[i][j-1])+b[i][j];
            }
        }
        printf("%d\n",f[n][m]);
    }
    return 0;
}

DP 最长上升子序列(板子)

  #include<stdio.h>
    #include <iostream>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    int a[1000050];
    int ans[1000050];
    int main()
    {
        int n,i;
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
        ans[1]=a[1];
        int len=1;
        for(i=2;i<=n;i++)
        {
            if(a[i]>ans[len])
                ans[++len]=a[i];
            else
            {
                int num=upper_bound(ans+1,ans+len+1,a[i])-ans;
                ans[num]=a[i];
            }
        }
        printf("%d\n",len);
        return 0;
    }

DP 最长公共子序列(板子)

复杂度O(n*n)

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int> p;
typedef long long LL;
char s1[105],s2[105];
int dp[105][105];
int main()
{
    scanf("%s",s1);
    scanf("%s",s2);
    int l1=strlen(s1);
    int l2=strlen(s2);
    for(int i=0;i<=l1;i++)
        for(int j=0;j<=l2;j++)
        {
            if(s1[i]==s2[j])
                dp[i+1][j+1]=dp[i][j]+1;
            else
                dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
        }
    printf("%d\n",dp[l1][l2]);
    return 0;
}

复杂度O(n*logn)

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int maxn = 1501 ;
vector<int> location[26] ;
int c[maxn*maxn] , d[maxn*maxn] ;

inline int get_max(int a,int b) { return a > b ? a : b ; }

//nlogn 求lcs
int lcs(char a[],char b[])
{
    int i , j , k , w , ans , l , r , mid ;
    for( i = 0 ; i < 26 ; i++) location[i].clear() ;
    for( i = strlen(b)-1 ; i >= 0 ; i--) location[b[i]-'a'].push_back(i) ;
    for( i = k = 0 ; a[i] ; i++)
    {
        for( j = 0 ; j < location[w=a[i]-'a'].size() ; j++,k++) c[k] = location[w][j] ;
    }
    d[1] = c[0] ; d[0] = -1 ;
    for( i = ans = 1 ; i < k ; i++)
    {
        l = 0 ; r = ans ;
        while( l <= r )
        {
            mid = ( l + r ) >> 1 ;
            if( d[mid] >= c[i] ) r = mid - 1 ;
            else l = mid + 1 ;
        }
        if( r == ans ) ans++,d[r+1] = c[i] ;
        else if( d[r+1] > c[i] ) d[r+1] = c[i] ;
    }
    return ans ;
}

int main()
{
    char a[maxn] , b[maxn] ;
    while (~scanf("%s%s",a,b))
    {
        printf("%d",lcs(a,b));
    }
}

DP 最长公共子序列(板子)

复杂度O(n*n)

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int> p;
typedef long long LL;
char s1[105],s2[105];
int dp[105][105];
int main()
{
    scanf("%s",s1);
    scanf("%s",s2);
    int l1=strlen(s1);
    int l2=strlen(s2);
    for(int i=0;i<=l1;i++)
        for(int j=0;j<=l2;j++)
        {
            if(s1[i]==s2[j])
                dp[i+1][j+1]=dp[i][j]+1;
            else
                dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
        }
    printf("%d\n",dp[l1][l2]);
    return 0;
}

复杂度O(n*logn)

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int maxn = 1501 ;
vector<int> location[26] ;
int c[maxn*maxn] , d[maxn*maxn] ;

inline int get_max(int a,int b) { return a > b ? a : b ; }

//nlogn 求lcs
int lcs(char a[],char b[])
{
    int i , j , k , w , ans , l , r , mid ;
    for( i = 0 ; i < 26 ; i++) location[i].clear() ;
    for( i = strlen(b)-1 ; i >= 0 ; i--) location[b[i]-'a'].push_back(i) ;
    for( i = k = 0 ; a[i] ; i++)
    {
        for( j = 0 ; j < location[w=a[i]-'a'].size() ; j++,k++) c[k] = location[w][j] ;
    }
    d[1] = c[0] ; d[0] = -1 ;
    for( i = ans = 1 ; i < k ; i++)
    {
        l = 0 ; r = ans ;
        while( l <= r )
        {
            mid = ( l + r ) >> 1 ;
            if( d[mid] >= c[i] ) r = mid - 1 ;
            else l = mid + 1 ;
        }
        if( r == ans ) ans++,d[r+1] = c[i] ;
        else if( d[r+1] > c[i] ) d[r+1] = c[i] ;
    }
    return ans ;
}

int main()
{
    char a[maxn] , b[maxn] ;
    while (~scanf("%s%s",a,b))
    {
        printf("%d",lcs(a,b));
    }
}

** 免费馅饼(简单DP)**
题目:
都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼。说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的10米范围内。馅饼如果掉在了地上当然就不能吃了,所以gameboy马上卸下身上的背包去接。但由于小径两侧都不能站人,所以他只能在小径上接。由于gameboy平时老呆在房间里玩游戏,虽然在游戏中是个身手敏捷的高手,但在现实中运动神经特别迟钝,每秒种只有在移动不超过一米的范围内接住坠落的馅饼。现在给这条小径如图标上坐标:
为了使问题简化,假设在接下来的一段时间里,馅饼都掉落在0-10这11个位置。开始时gameboy站在5这个位置,因此在第一秒,他只能接到4,5,6这三个位置中其中一个位置上的馅饼。问gameboy最多可能接到多少个馅饼?(假设他的背包可以容纳无穷多个馅饼)

Input
输入数据有多组。每组数据的第一行为以正整数,表示有n个馅饼掉在这条小径上。在结下来的n行中,每行有两个整数x,T(表示在第T秒有一个馅饼掉在x点上。同一秒钟在同一点上可能掉下多个馅饼。n=0时输入结束。

Output
每一组输入数据对应一行输出。输出一个整数m,表示gameboy最多可能接到m个馅饼。
提示:本题的输入数据量比较大,建议用scanf读入,用cin可能会超时。

题解:
DP[i][j]代表i时刻在j位置接到的馅饼。
因为不知道最好停留的位置,所以时间逆序DP。
特判一下0位置,不然数组会越界。

代码:

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<iostream>
using namespace std;
int dp[100005][12];
int main()
{
    int n,i,j;
    while(~scanf("%d",&n))
    {
        if(n==0)break;
        int time=-1;
        memset(dp,0,sizeof(dp));
        for(i=0;i<n;i++)
        {
            int x,t;
            scanf("%d%d",&x,&t);
            dp[t][x]++;
            time=max(time,t);
        }
        for(i=time-1;i>=0;i--)
        {
            for(j=0;j<11;j++)
            {
                if(j==0)
                    dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+dp[i][j];
                else
                    dp[i][j] = max(max(dp[i+1][j],dp[i+1][j+1]),dp[i+1][j-1])+dp[i][j];
            }
        }
        printf("%d\n",dp[0][5]);
    }
    return 0;
}

HDU 2089 不要62 (数位DP)
大神的操作 当板子用…

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<string>  
using namespace std;  
typedef long long ll;  
int a[20];  
int dp[20][2];  
int dfs(int pos,int pre,int sta,bool limit)  
{  
    if(pos==-1) return 1;  
    if(!limit && dp[pos][sta]!=-1) return dp[pos][sta];  
    int up=limit ? a[pos] : 9;  
    int tmp=0;  
    for(int i=0;i<=up;i++)  
    {  
        if(pre==6 && i==2)continue;  
        if(i==4) continue;//都是保证枚举合法性  
        tmp+=dfs(pos-1,i,i==6,limit && i==a[pos]);  
    }  
    if(!limit) dp[pos][sta]=tmp;  
    return tmp;  
}  
int solve(int x)  
{  
    int pos=0;  
    while(x)  
    {  
        a[pos++]=x%10;  
        x/=10;  
    }  
    return dfs(pos-1,-1,0,true);  
}  
int main()  
{  
    int le,ri;  
    //memset(dp,-1,sizeof dp);可优化  
    while(~scanf("%d%d",&le,&ri) && le+ri)  
    {  
        memset(dp,-1,sizeof dp);  
        printf("%d\n",solve(ri)-solve(le-1));  
    }  
    return 0;  
} 

状压DP
https://mp.csdn.net/mdeditor/81407630

P1280 尼克的任务(DP)

这道题虽然不难,但是这个状态方程对于我来说还是很难想到。这道题要倒着DP,因为顺序的话不知道结束的状态是什么,所以考虑的因素很多。倒着的话,就两种情况。
一:当前无任务,休息一秒
二:当前有任务,考虑做不做
直接上代码。

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
using namespace std;
int a[100005]; //记录开始的时间
int b[100005]; //记录结束的时间
int dp[10005];
int main()
{
    int n,t,i,x,y;
    scanf("%d%d",&t,&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&y);
        a[i]=x;
        b[i]=y;
    }
    int num=n;
    for(i=t;i>=1;i--)
    {
        if(a[num]!=i)   dp[i]=dp[i+1]+1;
        //当前没有任务,所以休息一秒
        else
        {
            while(a[num]==i)
            {
                dp[i]=max(dp[i],dp[i+b[num]]);
                //当前有任务,考虑要不要做。
                num--;
            }
        }
    }
    printf("%d\n",dp[1]);
    return 0;
}

洛谷P1006 传纸条(DP)
很经典
题目描述
小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个 m 行 n 列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标 (1,1 ),小轩坐在矩阵的右下角,坐标 (m,n) 。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。

在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。

还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用 0 表示),可以用一个 0-100的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这 22 条路径上同学的好心程度之和最大。现在,请你帮助小渊和小轩找到这样的 2 条路径。

输入输出格式
输入格式:
输入文件,第一行有 2 个用空格隔开的整数 m 和 n ,表示班里有 m 行 n 列。

接下来的 m 行是一个 m×n 的矩阵,矩阵中第 i 行 j 列的整数表示坐在第 i行 j 列的学生的好心程度。每行的 n个整数之间用空格隔开。

输出格式:
输出文件共一行,包含一个整数,表示来回 2 条路上参与传递纸条的学生的好心程度之和的最大值。

输入输出样例
输入样例#1: 复制
3 3
0 3 9
2 8 5
5 7 0
输出样例#1: 复制
34

题解:
感觉很难理解…直到看到了这个题解。

因为是从上方和从下方传纸条,为了方便,我们相当于从左上角连续传两张纸条,路径不重复,效果相同。

从左上来看的话就只能向右或向下传纸条。
这里写图片描述

那么两张纸条在过程中就一定在一条斜线上,而在一条斜线上纵坐标与横坐标相加相等。
在如图的斜线中,两个点的和都为3.

首先重要的就是三维F数组。

第一维度维护的是在传的过程中纵坐标与横坐标的和。

在同一斜线上,剩下表示两个点的从坐标就可以表示这两个点的位置。
第二维度维护的是相对在左边的点的纵坐标。

第三维度维护的是相对在右边的点的纵坐标。

当查询一个情况时,只有四种情况可以到他

F[sum][i][j]=max{F[sum-1][i][j]+F[k-1][i][j-1]+F[k-1][i-1][j]+F[k-1][i-1][j-1];

最后再加上a数组里存的两个点的好感度即可

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=60;
int a[maxn][maxn];
int F[2*maxn][maxn][maxn];
int main()
{
  int m,n;
  scanf("%d%d",&m,&n);
  for(int i=1;i<=m;i++)
    for(int j=1;j<=n;j++)
      scanf("%d",&a[i][j]);
  //F[sum][i][j]=max{F[sum-1][i][j]...
  memset(F,-1,sizeof(F));//赋初值为-1 (原因在后面) 
  F[2][1][1]=0;//最初的点,在左上角,好感度为0 
  for(int k=3;k<m+n;k++)
    for(int i=1;i<n;i++)
      for(int j=i+1;j<=n;j++)
      {
        int s=F[k][i][j];
        if(F[k-1][i][j]>s)s=F[k-1][i][j];
        if(F[k-1][i-1][j]>s)s=F[k-1][i-1][j];
        if(F[k-1][i][j-1]>s)s=F[k-1][i][j-1];
        if(F[k-1][i-1][j-1]>s)s=F[k-1][i-1][j-1];
        if(s==-1)continue;//当s为-1时,说明四种情况都不能到该点,故不存在。 
        F[k][i][j]=s+a[k-i][i]+a[k-j][j];//该点的值为最大的前一个值与当前F[k][i][j]表示两点的值的和。 
      }
  printf("%d",F[m+n-1][n-1][n]);//因为i永远小于j,所以右下角的点不会求到,
  //但是到右下角只有一种情况,就是在右下角的上面和右下角的左边,直接输出就好了。 
  return 0;
 } 

P1387 最大正方形(DP)

题目:
在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长。
输入输出样例
输入样例#1: 复制
4 4
0 1 1 1
1 1 1 0
0 1 1 0
1 1 0 1
输出样例#1: 复制
2

题解:
只有当前位置为1时,正方形长度才可以更大。而要使得正方形最大,左,上,左上都为一,否则为他们三个里面最小的那个加1.所以状态转移方程为:
f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1;

代码:

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
int a[101][101];
int f[101][101];
int main()
{
    int n,m,i,j,maxx=-1;
    scanf("%d%d",&n,&m);
    memset(f,0,sizeof(f));
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
        {
            scanf("%d",&a[i][j]);
            if(a[i][j]==1)
                f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1;
            maxx=max(maxx,f[i][j]);
        }
    printf("%d\n",maxx);
    return 0;
}

P1025 数的划分(DP)

题目描述
将整数n分成k份,且每份不能为空,任意两个方案不相同(不考虑顺序)。

例如:n=7,k=3,下面三种分法被认为是相同的。

1,1,5;
1,5,1
5,1,1.

问有多少种不同的分法。

输入输出格式
输入格式:
n,k

输出格式:
1个整数,即不同的分法。

输入输出样例
输入样例#1: 复制
7 3
输出样例#1: 复制
4
说明
四种分法为:
1,1,5;
1,2,4;
1,3,3;
2,2,3.

题解
首先感叹真的是弱。这个DP题状态转移方程真的是想不出来。直到我看到了这道题的另一个题目。。
这题其实是排列组合里的题,可以把一个数值为n的数当做n个小球,划分的份数k当做k个盒子,那么本题可以转化为“将n个小球放到k个盒子中,小球之间与盒子之间没有区别,并且最后的结果不允许空盒”

将n个小球放到k个盒子中的情况总数 =

a.至少有一个盒子只有一个小球的情况数

+b.没有一个盒子只有一个小球的情况数

这样进行划分是因为这种分类可以使a和b都有能写出来的表达式:

a.因为盒子不加区分,那么1的情况数与“将n-1个小球放到k-1个盒子中”的情况数一样

b.没有一个盒子只有一个小球,那么把每个盒子中拿出来一个小球,对应的是“把(n-k)个小球放到k个盒子中的情况数”

然后将上面的思路化为动态转移方程:

设f[n,k]代表将n个小球放到k个盒子中且没有空盒的情况,那么f[n,k] = f[n-1,k-1] + f[n-k,k]

而当k=1时只有1种方法(小球全部放进1个盒子)

代码

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
int dp[205][10];
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        dp[i][1]=1;
        dp[i][0]=1;
    }
    for(int j=2;j<=k;j++)
    {
        dp[1][j]=0;
        dp[0][j]=0;
    }
    for(int i=2;i<=n;i++)
    {
        for(int j=2;j<=k;j++)
        {
            if(i>j)
                dp[i][j]=dp[i-1][j-1]+dp[i-j][j];
            else
                dp[i][j]=dp[i-1][j-1];
        }
    }
    printf("%d\n",dp[n][k]);
    return 0;
}

洛谷P1052 过河(DP+路径压缩离散化)
题目描述
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,…,L(其中LL是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。

题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。

输入输出格式
输入格式:
第一行有1个正整数L(1 ~10^9)
),表示独木桥的长度。

第二行有3个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离及桥上石子的个数,

第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。

输出格式:
一个整数,表示青蛙过河最少需要踩到的石子数。

输入输出样例
输入样例#1: 复制
10
2 3 5
2 3 5 6 7
输出样例#1: 复制
2

题解
首先说状态转移方程
这是一个比较简单方程式。
首先设f[i]为在i点上的最少踩石子数,则在前面(i-s)到(i-t)的点都可以改变i点的值,因此我们可以取f[i-s]-f[i-t]之中的最小值,另外如果有石头就加上1,如果没有就不加值,这里我们直接用flag[i]表示该点有无石头(有则为1,无则为0)。
因此我们可以写出状态转移方程式 f[i]=min(f[i-j]+flag[i]|s<=j<=t)
然后因为n的数据太大,数组根本存不下,所以这个时候就要考虑把数据离散化,也就是路径压缩短。
因为n为1e9,但是s,t的范围是1-10,并且m<100,所以中间其实很多点都是没有石头的。然后我们不知道每一步怎么走,所以压缩的时候,如果两石头间距大于1-10的最小公倍数即2520,就求余2520,然后把l压缩短,就可以了。
最后注意一点,最后dp的结果不是dp[l],因为走到l位置不一定是最优解,所以要取l到l+r-1直接的最优解。

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<iostream>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define maxn 1000500
using namespace std;
typedef unsigned long long LL;
int a[105];//地图
int d[105];//间距
int dp[500005];//dp
int dis[500005];//判断该点是否有石头
int main()
{
    int l,s,t,m;
    scanf("%d%d%d%d",&l,&s,&t,&m);
    for(int i=1;i<=m;i++)
        scanf("%d",&a[i]);
    sort(a+1,a+1+m);
    for(int i=1;i<=m;i++)
        d[i]=(a[i]-a[i-1])%2520;//要对1~10的最小公倍数取余,压缩路径的核心
    for(int i=1;i<=m;i++)
    {
        a[i]=a[i-1]+d[i];
        dis[a[i]]=1;
    }
    l=a[m];
    for (int i=0;i<=l+t;i++)
        dp[i]=m; //f[i]表示到位置i最少能踩到的石子数
    dp[0]=0;
    for(int i=1;i<l+t;i++)
    {
        for(int j=s;j<=t;j++)
        {
            if(i>=j)
                dp[i]=min(dp[i],dp[i-j]); //状态转移方程
        }
        dp[i]+=dis[i];
    }
    int ans=m;
    for(int i=l;i<l+t;i++)
    {
        ans=min(ans,dp[i]);
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值