好久没写总结了,问题积累了不少,把做过的题型分类总结一下了,下面这些题型也是我在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(刚学的时候感觉很神啊,呵呵),这种题也有模板,如下:
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快了不少。
#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省去了许多不必要的计算步骤,核心代码:
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, 也过了。核心代码:
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),这样就使问题简单了不少。核心代码如下,
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问题,但找不到突破点,后来上网一查,很出乎意料,竟尽然是数塔模型。只好借鉴了,到现在没其他思路。慢慢想吧。
代码如下,
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;
具体思路还在思考过程中。。。。。。