( ╯□╰ ),菜鸡看了一上午才看懂orz
1.学了这么多天DP,对递推关系也有了一些认识:在想这个递推关系的时候先不要顾及边界,只想递推关系中的一个节点和之前的节点之间的关系就好,递推的初始状态之后再另外想。
2.划分数这个题,(0,0,2)也是一种正确的划分。这个在后面的推导过程中是可以看出来的。
3.可以先看一下这个链接上的讲解:点击打开链接
开始敲黑板了!!!
4.一定要结合实例理解,这是理解这些东西的最好的工具,别问我是怎么知道的。代码注释里有我用书上的实例推导的一个结果,代码先了解一下,主要是后面的注释:
#include <cstdio>
int n, m, M;
const int MAX_M = 10000;
const int MAX_N = 1000;
int dp[MAX_M + 1][MAX_N + 1];
void print(int row, int col)
{
printf("i/j |");
for (int i = 0; i <= col; i++)
printf("%4d", i);
printf("\n-----");
for (int i = 0; i <= col; i++)
printf("----");
for (int i = 0; i <= row; i++)
{
printf("\n%3d |", i);
for (int j = 0; j <= col; j++)
printf("%4d", dp[i][j]);
}
}
int main()
{
freopen("E:/My/input.txt", "r", stdin);
scanf("%d%d%d", &n, &m, &M);
printf("n = %d, m = %d, M = %d\n", n, m, M);
dp[0][0] = 1;
for (int i = 1; i <= m; i++)
for (int j = 0; j <= n; j++)
if (j - i >= 0)
dp[i][j] = (dp[i - 1][j] + dp[i][j - i]) % M;
else
dp[i][j] = dp[i - 1][j];
print(m, n);
return 0;
}
/*
n = 4, m = 3, M = 10000
i/j | 0 1 2 3 4
-------------------------
0 | 1 0 0 0 0
1 | 1 1 1 1 1
2 | 1 1 2 2 3
3 | 1 1 2 3 4
i/j | 0 1 2 3 4
----------------------------------------------------------------------------------------------------------------------
0 | 1 0 0 0 0
1 | 1{(0)} 1{(1)} 1{(2)} 1{(3)} 1{(4)}
2 | 1{(0,0)} 1{(0,1)} 2{(0,2),(1,1)} 2{(0,3),(1,2)} 3{(0,4),(1,3),(2,2)}
3 | 1{(0,0,0)} 1{(0,0,1)} 2{(0,0,2),(0,1,1)} 3{(0,0,3),(0,1,2),(1,1,1)} 4{(0,0,4),(0,1,3),(0,2,2),(1,1,2)}
*/
如上图,拿dp[3][4]来说。红框之内的东西便是dp[i - 1][j],发现了吧,就是在dp[2][4]的每个元素前面加了个0。而黑框内的就是dp[i][j - i],即dp[3][1],每个值减了1。要想严密的分解dp的元素,可以把dp[3][4]中的()分解为两种,一种是以0打头的(也就是dp[i - 1][j]加了个0,以0打头说明这串数中至少有一个0,所以)另一种就是一个0也没有的,这两类数对应上面的两个不同颜色的框,所以也就能理解了为什么作者这么分了:dp[i][j] = dp[i - 1][j] + dp[i][j - i]。
接下来是初始状态的赋值,这题i = 0着实令人有些费解,所以可以直接从i=1开始赋值,把i = 1这行的都赋值为1,memset当然不行了所以直接循环赋值,循环的时候直接从i=2开始。
NICE...