总结一类比较特别dp模型

hdu1421 poj2228hdu2577 这三个题目是最近做到的比较有特点的一类 由于某一些条件 比如取了一个东西 相邻的东西就会有所改变 那么一般会在朴素的转移方程上再加一维状态 0 1 代表对这一项不操作或者 操作  然后再从这个状态的意义出发推方程 会相对好做一些 

hdu1421 搬寝室 经典DP模型 分析一下可以转化为 n个有序数字 取k个相邻的两个数字  代价是这两个数字的差的平方

一开始想了很久 感觉还有点像区间DP 后来仔细一想不管复杂度还是状态都是不好做 经过大神指点 dp[i][j]代表前i个数字取j个相邻的两个数字 然后加一维[0][1]分别代表取与不取 然后推出方程 

dp[i][j][0] = min( dp[i-1][  j ][ 0 ] ,dp[ i - 1 ][ j ][1] )
 dp[i][j][1] = dp[i-1][j-1][0] + (num[i] - num[i-1]) * (num[ i ] - num[i - 1])

然后再仔细一看发现[1]这个状态是固定的可以递推出来的 带进去可以把dp[ i ][ j ][ 1 ]消去,

代码如下(代码里面我没有把状态[ 1 ]都消去)

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<fstream>
#include<stack>
#include<queue>
#include<iostream>
#include<set>
using namespace std;
const int   INF=0x3f3f3f3f;
const int MOD=1e9+7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define writeln(x) cout<<x<<endl
#define readln(x) cin>>x
#define  LONG long long
#define rep(a,b,c)  for(LONG a=b;a<=c;a++ )
#define rrep(a,b,c)  for(LONG a=b;a>=c;a--)
#define EPS 1e-10
int dp[2010][1005][2];          //0代表以i结尾的东西要搬  1代表不搬
int num[2200];
int minx[2200];
int main()
{
    int n , k;
    while(~scanf("%d%d",&n,&k))
    {
        for(int i = 1 ;i <=n; i++)
            scanf("%d",&num[i]);
        sort(num+1,num+n+1);
        clr1(dp);
        for(int i =1; i<= n ;i++)dp[i][0][0] = 0;   // 初始化我也不太会 应该从逻辑意义上去·初始化 可惜我一般只会面向答案和转移方程初始化
            //这里的意义代表应该是前i项什么都不搬的时候  消耗都为0
        for(int i = 1; i<= n  ; ++ i)
            for(int j = 1; j<= i/2 ; ++ j)
            {
                dp[i][j][0] = min(dp[i-1][j][0] , dp[i-2][j-1][0] + (num[i-1]-num[i-2]) * (num[i-1]-num[i-2]) );
                dp[i][j][1] = dp[i-1][j-1][0] + (num[i]-num[i-1]) * (num[i]-num[i-1]);
            }
        printf("%d\n",min(dp[n][k][0],dp[n][k][1]));
    }
}
poj2228  讲的是一头母牛睡觉的故事 总的来说 给n个数字  取m个数字 但是如果是某一连续段中第一个取的话 这个数字的值是不加到总的res里面的 比如1 2  3 4 5 6 我们选4 5  6 ,4是不加进去的 如果单独选6 ,6也是不加进去的 还有比较麻烦的是环形情况  这个地方跟上面一样 加一维【0】【1】,可以推出方程

                    dp[i][j][0]=max(dp[i-1][j][1] , dp[i-1][j][0]);
                    dp[i][j][1]=max(dp[i-1][j-1][1]+uti[i] , dp[i-1][j-1][0]);

dp[i][j][0,1]代表前i个数字取了j个数字且第j个数字不取或者取的最优解  由于环形情况代码混乱 就不贴代码了 


hdu2577 比较简单的DP 打印一些大写或者小写字母 可以用capslock 也可以用shift 问怎么样使得打字次数最少

同样的dp[i][0,1]代表前i个按Lock或者shift的最优解 

然后进行方程转移

                if(str[i-1]>='a'&&str[i-1]<='z')
                {
                    dp[i][0]=min(dp[i-1][0]+1,dp[i-1][1]+2);
                    dp[i][1]=min(dp[i-1][1]+2,dp[i][0]+2);
                }
                else
                {
                    dp[i][0]=min(dp[i-1][0]+2,dp[i-1][1]+2);
                    dp[i][1]=min(dp[i-1][0]+2,dp[i-1][1]+1);
                }
感觉做了有一些DP 但是还不够敏感 应该继续加强训练

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值