解题报告
题目
数字(number)
有n个数字(0到99)排成一行,每一次可以将相邻的两个数字相加并对100取模(即除以100的余数),将结果取代之前的两个数,一次操作的花费为两个数字相乘。经过n-1次操作后剩下一个数,问剩下一个数时总花费的最小值。
问题分析:
举例模拟
3 2 1 3 5
子问题的寻找
假设数据如下,已经到最后一阶段,假设子问题会用魔法完成,不需要费用,会有几种情况
10
30 20 0 90 80 60 40 30 10 20
1 2
30 350
10500
2 3
50 330
16500
3 4
50 330
16500
4 5
140 240
33600
5 6
220 160
35200
6 7
280 100
28000
7 8
320 60
19200
8 9
330 50
16500
9 10
340 40
13600
使用(1,2)
分割最省
同时此问题满足最优子问题性质
所以可以用分治法
子问题:如合并i,子问题是DFS(S,i) 和 DFS(i,E)
;
式:DFS(S,E)=DFS(S,i)+DFS(i,E)+Sum(S,i)*Sum(i,E)
;
基线条件:
if E - S == 2 return Num[S] * N
if E - S == 1 return 0
可以使用DFS,相当于石子合并
纯搜索复杂度 O(N^N)
显然会超时
但由于时间复杂度原因,要使用记忆化DFS,解决重复子问题,降低复杂度
代码
#include <bits/stdc++.h>
using namespace std;
int nums[105], x;
int nb[105][105];
int sum(int s, int e) {
int ret = 0;
for (int i = s; i < e; i++) {
ret += nums[i];
ret %= 100;
}
return ret;
}
int dfs(int s, int e) {
//if(s==e) return 0;
if (nb[s][e] != 0x7f7f7f7f) {
return nb[s][e];
}
if (e - s == 2) {
nb[s][e] = nums[s] * nums[e - 1];
return nums[s] * nums[e - 1];
}
if (e - s == 1) {
nb[s][e] = 0;
return 0;
}
//_min=0x7f7f7f7f;
for (int i = s + 1; i < e; i++) {
x = dfs(s, i) + dfs(i, e) + (sum(s, i) * sum(i, e));
if (x < nb[s][e]) nb[s][e] = x;
}
//nb[s][e]=_min;
return nb[s][e];
}
int main() {
//freopen("number.in","r",stdin);
//freopen("number.out","w",stdout);
int n;
while (cin >> n) {
memset(nb, 0x7f, sizeof(nb));
memset(nums, 0, sizeof(nums));
for (int i = 0; i < n; i++) {
scanf("%d", &nums[i]);
}
printf("%d\n", dfs(0, n));
}
return 0;
}