dp(题型小结)

   好久没写总结了,问题积累了不少,把做过的题型分类总结一下了,下面这些题型也是我在hdu acm step里面碰到的类型题,重新梳理一下思路。

题型一:

http://acm.hdu.edu.cn/showproblem.php?pid=1003

hdu1003思路前段时间已经叙述过了,在这里重点说一下这种题型的入手点,

 一看这种题型就是属于求最大子序列的和,切入点就是找到start和end,即起始位置以及结束位置。起初,我碰到这道题的时候,也是借鉴网上的一些办法,不过随着学习的不断深入,也逐渐有了点头绪,其实思路还是和网上的基本一致,但这只是借鉴和学习,并没有真正做到融会贯通,这还需要时间吧。希望在以后的学习中能加入自己的一些点子。具体思路看我的博客吧!

题型二:

http://acm.hdu.edu.cn/showproblem.php?pid=1159

类似1003,但又有所不同,求解最大公共子序列的长度。也相当于是上一种的变形了,也就是传说中的LCS(刚学的时候感觉很神啊,呵呵),这种题也有模板,如下:

ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
void LCS( char a[], char b[], int x, int y)
{
int i,j;
for (i = 0 ;i <= x;i ++ )
c[i][
0 ] = 0 ;
for (j = 0 ;j <= y;j ++ )
c[
0 ][j] = 0 ;

for (i = 1 ;i <= x;i ++ )
{
for (j = 1 ;j <= y;j ++ )
{
if (a[i - 1 ] == b[j - 1 ]) {c[i][j] = c[i - 1 ][j - 1 ] + 1 ;}
else { c[i][j] = Max( c[i][j - 1 ], c[i - 1 ][j]);}
}
}
}

这是hdu课件里边的方法,方法不错,也就拿来学习了,这种思维对于惯性思维来说还是有挑战性的,当时我为了弄明白这个题,整整花了一个下午的时间。到最后终于知道是怎么回事了。

   其中,c[i][j]表示a中的第i个字符和b中的第j个字符相比较,结果为两串相比较前的a中前i个字符和b中前j个公共字符的长度,c[i][j]=c[i-1][j-1]+1,表示在a[i-1]和b[j-1]相等的情况下,使其相等字符的个数加1,即c[i-1][j-1]+1,否则,c[i][j]=Max( c[i][j-1], c[i-1][j]),解释为在a中的第i个和b中第j个不相等的时候,那么就需要从前面a中的第i个和b中的第j-1个的值以及a中的第i-1个和b中的第j个寻找一个最大的赋给c[i][j],这样方便下一次比较。

   这里面就有很强的动态性,即最大子序列长度的传递性,利用二维数组,将本来间断的关系线性化。

 for(i=0;i<=x;i++)
        c[i][
0]=0;
   
for(j=0;j<=y;j++)
        c[
0][j]=0;
这是在没有比较之前,结果都为0。

题型三:

http://acm.hdu.edu.cn/showproblem.php?pid=1087

这个题,刚开始我用dfs做的,结果超时,剪枝也不好使。后来估计这可能是dp, 根据dfs的思路一想,后来也看了网上的报告,的确是!

这是我刚开始用dfs做的,其实dp和dfs的思路一个样,只是从时间上比dfs快了不少。

ContractedBlock.gifdfs
 
   
#include " iostream "
using namespace std;
int a[ 1010 ];
int n,sum;
int p = 0 ;
void dfs( int x, int y)
{
while (y <= x && x < n) y ++ ;

if (a[x] < a[y] && y < n && x < n)
{
sum
+= a[y];
x
= y;
if (x < n - 1 && y < n - 1 ) dfs(x,y);
}
else if (y < n - 1 ){ y ++ ; if (y < n - 1 ) dfs(x,y);}
}
int main()
{
int i,j;
while (cin >> n,n)
{
for (i = 0 ;i < n;i ++ ) cin >> a[i];
int Max =- 1 ;
sum
= 0 ;
for (i = 0 ;i < n;i ++ )
{
sum
= a[i];
if (Max < sum) Max = sum;
for (j = i + 1 ; j < n; j ++ )
{
p
= 0 ;
if (a[i] < a[j])
{
sum
+= a[j];
dfs(j,p);
}
if (sum > Max) Max = sum;
sum
= a[i];
}
}
cout
<< Max << endl;
}
return 0 ;
}

后来,看了一下网上的解题报告,很快思路就有了。

状态转移方程  b[i]=max(b[i], b[j]+a[i]),关键点,比dfs省去了许多不必要的计算步骤,核心代码:

ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
for (i = 1 ;i < n;i ++ )
{
b[i]
= a[i];
for (j = 0 ;j < i;j ++ )
if (a[j] < a[i] && b[i] < b[j] + a[i])
b[i]
= b[j] + a[i];
if (b[i] > Max) Max = b[i];
}


这个也容易理解,a[j]<a[i]是判断棋子是否是按照递增的顺序走的,至于b[i]<b[j]+a[i],是判断前i段的和和前j段的和加上第i个数是比较,找一个最大的。

最后和Max比较即可求出最大值。

题型四:

http://acm.hdu.edu.cn/showproblem.php?pid=1160

早上好!继续了!

   这道题思路简单,刚开始用搜索做的结果一直w,不知道为什么,当时看数据量也不是很大,1000也不大啊,时间复杂度最带也就是O(n^2),其实真正运行达不到这个时间,估摸着又是dp了,后来借鉴网上的方法,改为dp, 也过了。核心代码:

ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
for (i = 1 ;i < k;i ++ )
{
a[i]
= 1 ; b[i] =- 1 ;
for (j = i - 1 ;j >= 1 ;j -- )
{
if (s[i].w > s[j].w && s[i].v < s[j].v && a[j] + 1 > a[i])
{
a[i]
= a[j] + 1 ;
b[i]
= j;
}
}

if (a[i] > Max)
{
Max
= a[i];
flag
= i;
}
}

状态转移方程:

a[i]=a[j]+1;
      b[i]=j;  其实,b[i]用于标记路径。同前面几个一样,a[i]记录的是最长递增子序列。条件限制 s[i].w>s[j].w && s[i].v<s[j].v && a[j]+1>a[i],表示为在质量严格递增,速度严格递减的情况下,比较a[i]和a[j]+1。

题型五:

http://acm.hdu.edu.cn/showproblem.php?pid=2571

   原来这是个系列题,挺有意思,有兴趣的可以做一做,网址:http://acm.hdu.edu.cn/search.php?field=problem&key=ACM。前段时间在poj上做过一个类型题,http://poj.org/problem?id=2704 不过这个题里面已经给出状态转移方程,dp[i][j],dp[i+1][y],dp[i][k*y](k>1),这样就使问题简单了不少。核心代码如下,

ContractedBlock.gif ExpandedBlockStart.gif View Code
  
for (i = 1 ;i <= n;i ++ )
{
for (j = 1 ;j <= m;j ++ )
{
if (j != 1 ) dp[i][j] = Max(dp[i][j], dp[i][j - 1 ] + a[i][j]);
if (i != 1 ) dp[i][j] = Max(dp[i][j] , dp[i - 1 ][j] + a[i][j]);

for ( int p = 2 ;p <= m; p ++ )
{
if (j * p > m) break ;
dp[i][j
* p] = Max(dp[i][j] + a[i][j * p] , dp[i][j * p] );
}
}
}

题型6:

http://acm.hdu.edu.cn/showproblem.php?pid=1176

 开始没什么思路,这道也是dp问题,但找不到突破点,后来上网一查,很出乎意料,竟尽然是数塔模型。只好借鉴了,到现在没其他思路。慢慢想吧。

代码如下,

ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
for (i = 1 ;i <= n;i ++ )
{
cin
>> a >> b;
dp[b][a]
++ ;
if (Max < b) Max = b;
}

for (i = Max ; i >= 1 ; i -- )
{
for (j = 0 ;j <= 10 ; j ++ )
{
if (j == 0 )
dp[i
- 1 ][j] += MAX(dp[i][j], dp[i][j + 1 ]);
else if (j == 10 )
dp[i
- 1 ][j] += MAX(dp[i][j - 1 ],dp[i][j]);
else
dp[i
- 1 ][j] += MAX(MAX(dp[i][j - 1 ], dp[i][j]),dp[i][j + 1 ]);
}
}
cout
<< dp[ 0 ][ 5 ] << endl;

具体思路还在思考过程中。。。。。。

     

 

转载于:https://www.cnblogs.com/FCWORLD/archive/2011/05/04/2036948.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值