一道非常经典的区间DP。 一开始没有想明白状态到底该如何表示,因为杀掉一只狼会影响其他狼的攻击力,所以看起来貌似非常难以解决。
但是如果看出了是区间DP之后,就可以展开DP的常规推理了。 首先,是状态的表示,如何表示状态,这是最重要的一步。 表示出状态以后,还要看他是怎么转移的,以及转移过去的子问题是不是和原问题具有相同的结构和意义。 我们套用区间DP常规的状态表示:d[i][j] 表示杀死第i~j头狼所受到的最小伤害。 那么对于每一层,枚举区间分割点k,将原区间分解成子区间,然后表示先打k两边的狼再杀k。 那么对于区间i~j,由于表示杀死了区间所有的狼,那么肯定会受到第i-1和j+1头狼的伤害加成。
可是这样表示真的是对的吗? 其实通过不断的分解子问题,而每个子问题确实又都是最优结构且无后效性。
细节参见代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 205;
const int INF = 1000000000;
int T,n,kase=0,a[maxn],b[maxn],d[maxn][maxn];
int dp(int i, int j) {
if(i > j) return 0;
int& ans = d[i][j];
if(ans != -1) return ans;
ans = INF;
for(int k=i;k<=j;k++)
ans = min(ans,dp(i,k-1)+a[k]+dp(k+1,j));
if(ans != INF)
ans += (b[i-1] + b[j+1]);
return ans;
}
int main() {
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
memset(d,-1,sizeof(d));
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
a[n+1] = b[n+1] = 0;
int ans = dp(1,n);
printf("Case #%d: %d\n",++kase,ans);
}
return 0;
}