SDNU-1045.石子合并1

题目大意

题目链接

有n堆石子排成一行,每次选择相邻的两堆石子,将其合并为一堆,记录该次合并的得分为两堆石子个数之和。已知每堆石子的石子个数,求当所有石子合并为一堆时,最小的总得分。

第一行一个整数n(1 <= n <= 200),表示石子堆数; 第二行n个整数a(1 <= a <= 100),表示每堆石子的个数。

分析

妥妥的区间dp。
f[i][j] 代表的是合成区间[i, j]的石子最小的分。
g(i, j) = sum[j] - sum[i - 1]
sum[i] 为前缀和
转移方程f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + g(i, k) + g(k + 1, j))

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <bits/stdc++.h>
#define eps 1e-8
#define endl "\n"
using namespace std;
#define ms(a, b) memset((a), (b), sizeof(a))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int N = 1e6 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const ll ll_max = 0x3f3f3f3f3f3f3f3f;

int f[1003][1003];
int a[1003];
int sum[1003];
inline int g(int i, int j){
return sum[j] - sum[i - 1];
}
int main(){
int n;
while(cin >> n){
memset(f, INF, sizeof f);
for (int i = 0; i < n; i++){
cin >> a[i];
f[i][i] = 0;
if (i == 0) sum[i] = a[i];
else sum[i] = sum[i - 1] + a[i];
}
for (int len = 1; len < n; len++){ //len代表区间的长度
for (int i = 0; i < n; ++i){ //i代表区间的左端点
int j = i + len; // j 代表曲线右端点
if (j >= n) continue;
for (int k = i; k <= j; ++k)
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + g(i, k) + g(k + 1, j));
}
}
cout << f[0][n - 1] << "\n";
}
return 0;
}

SDNU-1048.石子合并2

题目大意

题目链接

有n堆石子排成一圈,每次选择相邻的两堆石子,将其合并为一堆,记录该次合并的得分为两堆石子个数之和。已知每堆石子的石子个数,求当所有石子合并为一堆时,最小的总得分。

第一行一个整数n(1 <= n <= 200),表示石子堆数; 第二行n个整数a(1 <= a <= 100),表示每堆石子的个数,这些石子首尾相接排成一圈。

分析

环的话那就把数组copy一遍放在原数组后面, 取结果的话, 就从0~n - 1到 n - 1 ~ 2 * n - 1, 取个最小值, 因为都有可能。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <bits/stdc++.h>
#define eps 1e-8
#define endl "\n"
using namespace std;
#define ms(a, b) memset((a), (b), sizeof(a))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int N = 1e6 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const ll ll_max = 0x3f3f3f3f3f3f3f3f;

int f[1003][1003];
int a[1003];
int sum[1003];
inline int g(int i, int j){
return sum[j] - sum[i - 1];
}
int main(){
int n;
while(cin >> n){
memset(f, INF, sizeof f);
for (int i = 0; i < n; i++){
cin >> a[i];
f[i][i] = 0;
if (i == 0) sum[i] = a[i];
else sum[i] = sum[i - 1] + a[i];
}
for (int i = n; i < 2 * n; ++i){
f[i][i] = 0;
sum[i] = sum[i - 1] + a[i % n];
}

for (int len = 1; len < 2 * n; len++){ //len代表区间的长度
for (int i = 0; i < 2 * n; ++i){ //i代表区间的左端点
int j = i + len; // j 代表曲线右端点
if (j >= 2 * n) continue;
for (int k = i; k <= j; ++k)
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + g(i, k) + g(k + 1, j));
}
}
int ans = INF;
for (int i = 0; i < n; ++i)
ans = min(ans, f[i][i + n - 1]);
cout << ans << "\n";
}
return 0;
}
恰似你一低头的温柔,较弱水莲花不胜寒风的娇羞, 我的心为你悸动不休。  --mingfuyan

千万不要图快——如果没有足够的时间用来实践, 那么学得快, 忘得也快。