下面是在别的地方看到的(虽然不知道准不准确先转再说……):
区间动态规划问题一般都是考虑,对于每段区间,他们的最优值都是由几段更小区间的最优值得到,是分治思想的一种应用,将一个区间问题不断划分为更小的区间直至一个元素组成的区间,枚举他们的组合 ,求合并后的最优值。
设F[i,j](1<=i<=j<=n)表示区间[i,j]内的数字相加的最小代价
最小区间F[i,i]=0(一个数字无法合并,∴代价为0)
每次用变量k(i<=k<=j-1)将区间分为[i,k]和[k+1,j]两段
For p:=1 to n do // p是区间长度,作为阶段。
for i:=1 to n do // i是穷举的区间的起点
begin
j:=i+p-1; // j是 区间的终点,这样所有的区间就穷举完毕
if j>n then break; // 这个if很关键。
for k:= i to j-1 do // 状态转移,去推出 f[i,j]
f[i , j]= max{f[ i,k]+ f[k+1,j]+ w[i,j] }
end;
这个结构必须记好,这是区间动态规划的代码结构。
nyoj 746:点击打开链接
状态转移方程:dp[i][j] = max(dp[i][j], dp[k][j - 1] * num[k + 1][i]) 定义dp[i][j]为将下标到i为止的数字串分成j个部分后乘积的最大值。
k是起点和终点之间的一个分割点, dp[k][j - 1]是将前面下标到k为止的数字串分成j - 1个部分所得的乘积的最大值, 为什么是j - 1呢,因为k就是一个分割点。num[k + 1][i]是k + 1位置到i位置的对应数的乘积。
通过枚举分割点的位置找到乘积的最大值。
#include <stdio.h>
#include <string.h>
#define MAX 10000000
int m;
char n[25];
long long num[25][25];
long long dp[25][25];
void change()
{//将每一段对应的数字存储到num数组中
int i, j, len = strlen(n);
int k = 0;
for(i = 0; i < len; i++)
{
k = 0;
for(j = i; j < len; j++)
{
k = k * 10 + (n[j] - 48);
num[i][j] = k;
}
}
}
long long max(long long a, long long b)
{
return a > b ? a : b;
}
int main (void)
{
int t;
scanf("%d", &t);
while(t --)
{
memset(dp, 0, sizeof(dp));
scanf("%s %d", n, &m);
change();
int i, j, k, len = strlen(n);
for(i = 0; i < len; i++)
//将0-i位置的数分成1份所得的最大值就是这个数本身
dp[i][1] = num[0][i];
for(i = 0; i < len; i++)
{//当前划分的长度为i
for(j = 1; j <= m; j++)
{//划分成j个部分
for(k = 0; k <= i; k++)//划分的位置
dp[i][j] = max(dp[i][j], dp[k][j - 1] * num[k + 1][i]);
}
}
printf("%lld\n", dp[len - 1][m]);
}
return 0;
}
poj 2955:点击打开链接
dp[i][j]表示起点为i终点为j的匹配的括号数的最大值。
#include <stdio.h>
#include <string.h>
char s[110];
int dp[110][110];
int yes(char a, char b)
{
if(a == '(' && b == ')')
return 1;
if(a == '[' && b == ']')
return 1;
return 0;
}
int max (int a, int b)
{
return a > b ? a : b;
}
int main (void)
{
while(scanf("%s", s) != EOF)
{
if(s[0] == 'e')
break;
int len = strlen(s);
memset(dp, 0, sizeof(dp));
int i, j, k, p;
for(i = 0; i < len - 1; i++)
if(yes(s[i], s[i + 1]))
dp[i][i + 1] = 2;//如果
for(p = 3; p <= len; p++)
{//p是字符串的长度,因为上面已经配对过相邻两个括号所以p从3开始
for(i = 0; i < len; i++)
{//i是起点
j = i + p - 1;//j为终点
if(j >= len)
break;
if(yes(s[i], s[j]))//如果匹配
dp[i][j] = dp[i + 1][j - 1] + 2;
//2是已经匹配的i位置和j位置的两个括号,再加上剩下的i+1位置到j-1位置字串的最大匹配数
for(k = i; k < j; k++)//k是分割点
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j]);
//i位置到k位置的匹配最大值加上k+1位置到j位置匹配的最大值
}
}
printf("%d\n", dp[0][len - 1]);
}
return 0;
}
Problem 111: 单词的划分
Time Limit:1 Ms| Memory Limit:64 MB
Difficulty:2
Description
Input
第二行一个整数n,表示单词的个数。(n<=100)
第3~n+2行,每行列出一个单词。
Output
Sample Input
5
real
reality
it
your
our
Sample Output
Hint
这个也是个区间动归题。
dp[i]为长度是i的单词所划分的最小单词数。
#include <stdio.h>
#include <string.h>
char s[110];
int n;
char word[110][50];
int dp[110];
int same(int x, int y)
{//从后向前比较
int i, j, l = strlen(word[y]);
for(i = 0; i < l; i++)
{
if(s[x - i] != word[y][l - 1 - i])
return 0;
}
return 1;
}
void init()
{
int i, j;
for(i = 0; i < 110; i++)
dp[i] = 10000000;
}
int min (int a, int b)
{
return a < b ? a : b;
}
int main (void)
{
while(scanf("%s", &s[1]) != EOF)
{
scanf("%d", &n);
int i, j;
for(i = 0; i < n; i++)
scanf("%s", word[i]);
int len = strlen(&s[1]);
init();
dp[0] = 0;
for(i = 1; i <= len; i++)
{//i是长度。注意i要从1开始,不然下面dp[i - 1]会出错
for(j = 0; j < n; j++)
{//j遍历所给的单词
int l = strlen(word[j]);
if(i < l - 1)
continue;
if(same(i, j))
{//如果能分成某个单词,就选择最小的
dp[i] = min(dp[i], dp[i - l] + 1);
}
}
}
/*
for(i = 0; i <= len; i++)
printf("%d ", dp[i]);
printf("\n");
*/
printf("%d\n", dp[len]);
}
return 0;
}
参考:点击打开链接