基础DP01(POJ1163, HDU1421,POJ2533)

来源:互联网 发布:淘宝捉猫猫四星怎么抓 编辑:程序博客网 时间:2024/06/04 20:31

DP(动态规划),又一个acm的核心专题, 主要是寻找状态转移的方程,不多说,上题


A.The Triangle



思路:这题是一道最基础的dp问题,求三角形最长路径,对于三角形中每个点,建立对应dp,表示从底端到达该点的最长距离,并从下往上开始转移

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


#include <iostream>#include <cstdio>#include <algorithm>#include <vector>#include <cstring>using namespace std;const int maxn = 1e4+5;int dp[maxn];int main(){    int n;    while(scanf("%d",&n)!=EOF)    {        memset(dp,0,sizeof(dp));        for(int i=0;i<n;i++)        {            int p;            scanf("%d",&p);            int now = dp[p] - 1;            if(now<0)            {                now = 0;            }            for(int j=0;j<p;j++)            {                if(dp[j]>now)                {                    now = dp[j];                }            }            dp[p] = now + 1;        }        int maxlen = 0;        for(int i=0;i<=1e4;i++)        {            if(dp[i]>maxlen)            {                maxlen = dp[i];            }        }        printf("%d\n",maxlen);    }    return 0;}


E.搬寝室



思路:这是一道经典的dp问题,根据需求从后往前递推,甚至有点背包的感觉。


不过,首先必须明白一个问题,那就是每次一定取重量相邻的两个物品,证明如下:

假设有四件物品a1<a2<a3<a4,则共有四种搬运的组合,分别为:1.a1a2,a3a4;2.a1a3,a2a4;3.a1a4,a2a3

因为a3-a1 = (a3-a2) + (a2-a1) > (a2-a1),a4-a2 = (a4-a3) + (a3-a2) > (a4-a3),所以,第一种方案优于第二种方案。

又因为a4-a1 = (a4-a3) + (a3-a2) + (a2-a1) >  (a4-a3) + (a2-a1),所以,第一种方案优于第二种方案。

可见,每次取相邻两个物品才为最优策略


于是,根据需求,便可以开始转移了,建立方程dp[k][p]表示前k个物品中搬了p对物品,则对于dp[k][p],即对第k个物品进行决策,使得前k个物品中搬了p对物品,于是对于第k个物品有两种情况,即搬与不搬:

1.如果搬第k个物品,则必定将第k-1个物品与其组成一对一同搬运,则需要在前k-2个物品中搬p-1对即可,再加上第k-1个物品和第k个物品疲劳度,即可得到当前疲劳度,即 dp[k-2][p-1] + tire[k-1];

2.如果不搬第k个物品,那么就简单了,只需要前k-1个物品中搬了p对即可,即dp[k-1][p];

综合上述两种情况,取最小值,即可得到状态转移方程dp[k][p] = min(dp[k-2][p-1] + tire[k-1] , dp[k-1][p]);


由于,总物品数可能超出int上限,因此采用long long存储数据。

此外,对于以1(非0)开头的数组,一定要注意其初始化以及处理的过程,避免出现不必要的错误。

不过,还需要考虑两种特殊情况:

1.对于dp[k][0],即前k个物品中一个都没有搬,显然dp[k][0] = 0;

2.对于k<p*2,即搬的物品数比总的物品数要多,这显然是不可能的,为了避免这种情况的发生,将该种情况的dp置为LONG_LONG_MAX


最后就是状态转移的过程中一定要注意避免转移到第二种不可能发生的特殊情况上,这样即可得到最终的结果


#include <iostream>#include <cstdio>#include <algorithm>#include <vector>#include <cstring>#include <climits>using namespace std;const int maxn = 2010;long long dp[maxn][maxn];long long tire[maxn];long long weight[maxn];int main(){    int n,k;    while(scanf("%d%d",&n,&k)!=EOF)    {        for(int i=1;i<=n;i++)        {            scanf("%lld",&weight[i]);        }        sort(weight+1,weight+n+1);        for(int i=2;i<=n;i++)        {            tire[i-1] = (weight[i] - weight[i-1]) * (weight[i] - weight[i-1]);        }        for(int i=0;i<=n;i++)        {            dp[i][0] = 0;            for(int j=i/2+1;j<=k;j++)            {                dp[i][j] = LONG_LONG_MAX;            }        }        for(int i=2;i<=n;i++)        {            for(int j=1;j<=k&&j<=(i/2);j++)            {                dp[i][j] = (dp[i-2][j-1] + tire[i-1]) < dp[i-1][j] ? (dp[i-2][j-1] + tire[i-1]) : dp[i-1][j];            }        }        printf("%lld\n",dp[n][k]);    }    return 0;}


B.Longest Ordered Subsequence



思路:这题又是个经典dp问题,不过是一个正向遍历dp。


设dp[i][j]表示加入第i个数字后以j作为最大的数据的最长有序子串长度。则每次只需要对新加的元素进行转移即可。

设第i个数据为k,则只需要对dp[i][k]进行转移即可。有两种转移方式:

1.由于原本以k作为最大值的子串已经很长了,因此该数据无法加入,需要舍弃,则dp值不变。

2.寻找出前k-1个最长子串的最大值,并将其与已有的dp[i-1][k]进行比较,如果不小,则说明该dp值需要更新。

因此可得状态转移方程dp[i][j] = max(dp[i-1][r] + 1 , dp[i-1][k]),其中r<k,且dp[i-1][r]为前k个dp中的最大值。


最后,转移完成之后,只需要遍历所有dp[n][i],并找出最大值即可,n为原序列长度,1<=i<=1000;


#include <iostream>#include <cstdio>#include <algorithm>#include <vector>#include <cstring>using namespace std;const int maxn = 1e4+5;int dp[maxn];int main(){    int n;    while(scanf("%d",&n)!=EOF)    {        memset(dp,0,sizeof(dp));        for(int i=0;i<n;i++)        {            int p;            scanf("%d",&p);            int now = dp[p] - 1;            if(now<0)            {                now = 0;            }            for(int j=0;j<p;j++)            {                if(dp[j]>now)                {                    now = dp[j];                }            }            dp[p] = now + 1;        }        int maxlen = 0;        for(int i=0;i<=1e4;i++)        {            if(dp[i]>maxlen)            {                maxlen = dp[i];            }        }        printf("%d\n",maxlen);    }    return 0;}