HDU-3506-Monkey Party
经典区间dp拓展。
在石子合并基础上化曲为直即可。
题目大意:围成一个圆形的一堆猴子要相互认识,问他们相互认识的最小代价。其中两猴子相识的代价等于他们各自相互认识的猴子的代价总和。如果两个猴子相识那么代表着他们原先各自的朋友也相互相识。这个代价就相当于石子合并的时候各个堆的个数。代价。一样的道理
然后这个地方把圆形化为一条直线。我们直接把1~n-1的那些猴子补到n的后面就可以了。长度为2n-1。
最后维护答案就是维护长度为n的dp就可以啦。
在计算前缀和的时候我们把1~2n-1都计算一遍。
数据输入的时候记得保存一下最后移到后面的那些值。方便后面求和。有些数重复计算了不要紧。反正最后求得是区间和。总要减的。
石子合并那道题我说的很清楚啦~
这里就不多说啦~
代码部分:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
const int INF = 1e9 + 10;
int a[N];
int dp[N][N];
int s[N][N];
int sum[N];
int main()
{
int n;
while (~scanf ("%d", &n))
{
for (int i = 1; i <= n; i++)
{
scanf ("%d", &a[i]);
sum[i] = sum[i - 1] + a[i];
a[i + n] = a[i];
}
for (int i = n + 1; i < 2 * n; i++)
{
sum[i] = sum[i - 1] + a[i];
}
n = 2 * n - 1;
for (int i = 1; i <= n; i++)
{
dp[i][i] = 0;
s[i][i] = i;
}
for (int len = 1; len < n; len++)
{
for (int i = 1; i <= n - len; i++)
{
int j = i + len;
dp[i][j] = INF;
for (int k = s[i][j - 1]; k <= s[i + 1][j]; k++)
{
if (dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1] < dp[i][j])
{
dp[i][j] = dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1];
s[i][j] = k;
}
}
}
}
n = (n + 1) >> 1;
int ans = INF;
for (int i = 1; i <= n; i++)
{
ans = min(ans, dp[i][n + i - 1]);
}
cout << ans << endl;
}
return 0;
}