hdu 4939 Stupid Tower Defense 动态规划
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4939
塔防游戏 红塔绿塔蓝塔
当年我队友在不知道红塔必须放最后这个结论的情况下把这道题做出来了现在我细思恐极 仰慕至极Orz
写出来不是很难的题目 但是仔细想想 还是有很多值得推敲的地方
下面证明一下这个“常识”
反证法
a.如果得到一个最优解 然后在这个最优解中有一个红塔在一个绿塔前面 那么把这两个塔交换一下位置 必定可以得到一个更优的解 与假设的“当前是最优解”矛盾
b.如果得到一个最优解 然后在这个最优解中有一个红塔在一个蓝塔前面 那么把这两个塔交换一下位置 必定可以得到一个更优的解 与假设的“当前是最优解”矛盾
所以 在最优解中 红塔一定是在最后的
此题表意不清 注意绿塔和蓝塔都是当前格不生效、走过以后才生效的
用dp[i][j]表示前i个塔中有j个蓝塔(并且假定后面全是红塔)时 前i格能造成的总的最大伤害
【如果不知道红塔在最后这个结论 那么可能就会变成dp[i][j][k]来表示前i格的状态 这时候会MLE】
【可是我队友居然用dp1[i][j]和dp2[i][k]两个数组解决了这个矛盾 我也是吓尿了】
动态规划的意义在于 只需要枚举三种塔各自的数量 而不用枚举具体的摆法
当j==0时 dp[i][j]可以直接算
当j!=0时 dp[i][j]仅与dp[i-1][j-1]和dp[i-1][j]有关
算出dp[i][j]后再加上后面(n-i)格的伤害即可得到当前摆法的最大伤害 不断更新最大值
可以这样递推的原因在于 假如已经求得dp[i-1][?]的最优 那么接下来在求dp[i][?]的最优时 不可能打乱前(i-1)格的摆法
不管第i格是放什么塔,它的结果仅与前面的各塔总数有关,而与前面摆法无关
注意要算全是红塔的情况 Orz
像我这种写法就要把ans初始化为全是红塔的结果
#include <cstdio> #include <cstdlib> #include <ctime> #include <iostream> #include <cmath> #include <cstring> #include <algorithm> #include <stack> #include <set> #include <queue> #include <vector> using namespace std; typedef long long ll; const int maxn = 1510; ll dp[maxn][maxn]; int main() { //freopen("in.txt", "r", stdin); int T; scanf("%d", &T); for(int kase = 1; kase <= T; kase++) { ll n, x, y, z, t; scanf("%I64d%I64d%I64d%I64d%I64d", &n, &x, &y, &z, &t); ll ans = n * t * x; for(ll i = 1; i <= n; i++) { for(ll j = 0; j <= i; j++) { if(j == 0) dp[i][0] = dp[i-1][0] + y * (i-1) * t; else dp[i][j] = max(dp[i-1][j-1] + y * (i-j) * (t+z*(j-1)), dp[i-1][j] + y * (i-j-1) * (t+z*j)); ll tmp = dp[i][j] + (x + y*(i-j)) * (n-i) * (t + z*j); if(tmp > ans) ans = tmp; } } printf("Case #%d: %I64d\n", kase, ans); } return 0; }