Monkey Party
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 1884 Accepted Submission(s): 828
Problem Description
Far away from our world, there is a banana forest. And many lovely monkeys live there. One day, SDH(Song Da Hou), who is the king of banana forest, decides to hold a big party to celebrate Crazy Bananas Day. But the little monkeys don't know each other, so as the king, SDH must do something.
Now there are n monkeys sitting in a circle, and each monkey has a making friends time. Also, each monkey has two neighbor. SDH wants to introduce them to each other, and the rules are:
1.every time, he can only introduce one monkey and one of this monkey's neighbor.
2.if he introduce A and B, then every monkey A already knows will know every monkey B already knows, and the total time for this introducing is the sum of the making friends time of all the monkeys A and B already knows;
3.each little monkey knows himself;
In order to begin the party and eat bananas as soon as possible, SDH want to know the mininal time he needs on introducing.
Now there are n monkeys sitting in a circle, and each monkey has a making friends time. Also, each monkey has two neighbor. SDH wants to introduce them to each other, and the rules are:
1.every time, he can only introduce one monkey and one of this monkey's neighbor.
2.if he introduce A and B, then every monkey A already knows will know every monkey B already knows, and the total time for this introducing is the sum of the making friends time of all the monkeys A and B already knows;
3.each little monkey knows himself;
In order to begin the party and eat bananas as soon as possible, SDH want to know the mininal time he needs on introducing.
Input
There is several test cases. In each case, the first line is n(1 ≤ n ≤ 1000), which is the number of monkeys. The next line contains n positive integers(less than 1000), means the making friends time(in order, the first one and the last one are neighbors). The input is end of file.
Output
For each case, you should print a line giving the mininal time SDH needs on introducing.
Sample Input
8 5 2 4 7 6 1 3 9
Sample Output
105
Author
PerfectCai
Source
Recommend
zhengfeng
题目大意就是有一个环状的石头堆,求合并成一堆的最小代价(提炼的题意别见怪)
经典区间dp合并石头堆的变种,但是也没变多少。
难点在意如何把环状往线状转化。
显然如果我们要枚举出所有的情况只要在第n堆之后后再复制前n-1堆石头,形成一个2*n-1的线状石头堆就可以枚举出环状的所有情况。再根据dp[i][j]的定义(因人而异,这里是笔者的定义)为 合并第i堆到第j堆石头的最小代价。
那么问题就会转换为 2*n-1堆的石头选哪n堆合并为一堆代价最小。
显然答案就变成 min(dp[l][l+n-1]) l∈[1,n]
当然题目还有一个难点那就是题目数据量有点大,为1e3.
显然未优化的区间dp(O(n^3))不能过。这里笔者用四边形不等式优化降维到O(n^2)
四边形不等式方面请读者自行百度。这里不多赘述
下面是代码
#include<iostream>
#include<string>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
#include<queue>
#include<set>
#include<cstdio>
#include<functional>
#include<iomanip>
#include<cmath>
#include<stack>
using namespace std;
const int maxn = (int)(1e5) + 100;
const int inf = 0x3f3f3f3f;
const int mod = 2520;
const double eps = 1e-3;
typedef long long LL;
typedef unsigned long long ull;
int dp[2111][2111];
int sum[2111], a[2111];
int maxs[2111][2111];
int main() {
//freopen("E:\\test.txt", "r", stdin);
int n;
while (~scanf("%d", &n)) {
sum[1] = 0;
memset(dp, 0x3f, sizeof(dp));
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
sum[i] = sum[i - 1] + a[i];
dp[i][i] = 0;
maxs[i][i] = i;
}
for (int i = 1; i < n; i++) {
sum[i + n] = sum[i + n - 1] + a[i];
maxs[i + n][i + n] = i + n;
dp[i + n][i + n] = 0;
}
for (int len = 2; len <= n; len++) {
for (int i = 1; i <= 2 * n - 1; i++) {
int j = i + len - 1;
if (j > 2 * n - 1) continue;
for (int k = maxs[i][j-1]; k <= maxs[i+1][j]; k++) {
if (dp[i][j] > dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1]) {
dp[i][j] = dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1];
maxs[i][j] = k;
}
}
}
}
int ans = inf;
for (int i = 1; i <= n; i++) {
ans = min(ans, dp[i][i + n - 1]);
}
printf("%d\n", ans);
}
return 0;
}