动态规划是一个很神奇的东西,总结一下自己写动态规划是的一些盲区!
有时候在定义状态的时候,可以用一定的方法,可以达到对转移时的时间复杂度起到很大的优化作用,以下面的题目来说明这点。
Programmers working on a large project have just received a task to write exactly m lines of code. There are n programmers working on a project, the i-th of them makes exactly ai bugs in every line of code that he writes.
Let's call a sequence of non-negative integers v1, v2, ..., vn a plan, if v1 + v2 + ... + vn = m. The programmers follow the plan like that: in the beginning the first programmer writes the first v1 lines of the given task, then the second programmer writes v2 more lines of the given task, and so on. In the end, the last programmer writes the remaining lines of the code. Let's call a plan good, if all the written lines of the task contain at most b bugs in total.
Your task is to determine how many distinct good plans are there. As the number of plans can be large, print the remainder of this number modulo given positive integer mod.
The first line contains four integers n, m, b, mod (1 ≤ n, m ≤ 500, 0 ≤ b ≤ 500; 1 ≤ mod ≤ 109 + 7) — the number of programmers, the number of lines of code in the task, the maximum total number of bugs respectively and the modulo you should use when printing the answer.
The next line contains n space-separated integers a1, a2, ..., an (0 ≤ ai ≤ 500) — the number of bugs per line for each programmer.
Print a single integer — the answer to the problem modulo mod.
这个题目是昨晚CF的一道题
题意为,有n个程序猿,每个程序猿写一行程序会出现ai个BUG,现在又m行程序需要写,而且总的BUG的数目不能大于b,问有多少种方法。
我一看这道题,第一感觉就是dp,然后按照常规的状态定义方法得到dp[i][j][k],表示前i个人,做了j行程序,出现了k个BUG的方法数目,转移方程很简单,枚举第i个人写多少行就可以了,但是这样的复杂度为O(n*m*b*m),最坏情况下为500^4,显然超时,当时就想怎么在转移方程上优化(当然这个在很多情况下是值得考虑的,有的方程可以用一些数据结构来优化,如线段树,树状数组),这点也是我经常干的事。不过对于这道题,这种方法没有任何作用,因为即使你将一维优化到logn,还是会超时,而且这个题我想了1:30小时也没有想出来(真是水),今天早上起床,打开来继续想,真的无法优化。当时做的时候就想着怎么去掉一维,考虑了一下,发现每个人是要考虑的,行数也是要考虑的,至于BUG数,如果不考虑,最后就无法统计方法数目了,所以也觉得无奈,怎么办啊,实在没法了,就看了Tutorial,第一眼看题解的状态,和我的差不多,也是dp[i][j][k],可是它怎么转移那么简单,dp[i][j][k]+=dp[i-1][j][k]+dp[i][j-1][k-a[i]],这样就编程递推,时间复杂度直接降到O(n^3),太神奇了,怎么做到的,然后再去看它怎么定义状态的,它的定义和我的是一样的,那为什么可以这样转移呢?这个就是问题的所在,我们只要搞懂这个,就对着类方法有了很好的认识,可以看到它的转移时分两种情况,第一,第i个人不做任何事,第i个人至少做1行,这样的话,就感觉很自然了,而且也很好理解