#include <iostream>
using namespace std;
#define N 101
int main()
{
freopen("in.txt", "r", stdin);
int n;
int f[N][N]; // f[i][j] 表示从从第 i 堆起顺时针数 j 堆石子所花费的体力最少
int sum[N][N]; // sum[i][j] 表示从第 i 堆起顺时针数 j 堆石子的和
int d[N]; // d[n] 表示读入的 n 堆石子
memset(sum, 0, N*N*4);
// 状态转移方程:
// f[i][j] = min { f[i][k] + f[x][j-k] + sum[i][j] }
// 其中:1 <= i <= n , 2 <= j <= n
// x = (i+k-1)%n + 1
// 1 <= k <= j-1
int i, j;
int k, x, td, min = 0x7fffffff;
while( cin>>n )
{
for(i = 1;i <= n; i++)
cin>>d[i];
d[0] = d[n]; // 注:此句重要,因为在运算 (i+j-1)%n 时,如果 i+j-1 等于 n ,
// 那么结果将会是下标0,但我们是想得到下标为 n 。但一个数取模是不可能等于他本身的。
// 所以将错就错,干脆把 下标为 n 的值赋给下标为 0 数组。
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++) // d[0] = d[n]
{
sum[i][j] = d[(i+j-1)%n] + sum[i][j-1]; // 初始化 sum[i][j]
f[i][1] = 0; // 初始化一堆石子的“合并”,一堆石子并不存在合并,所以其值都为 0
}
}
// 注意:此处 j 放在外循环,即首先从合并 2 堆石子开始,逐步扩展到合并 n 堆石子。
for(j = 2; j <= n; j++)
for(i = 1; i <= n; i++)
{
// min { f[i][k] + f[x][j-k] + sum[i][j] } x = (i+k-1)%n + 1
min = 0x7fffffff;
for(k = 1; k <= j-1; k++)
{
x = (i+k-1)%n + 1;
if( min > f[i][k] + f[x][j-k] + sum[i][j] )
{
min = f[i][k] + f[x][j-k] + sum[i][j];
td = k; // 保存最佳分配时从哪一堆开始
}
}
f[i][j] = min;
}
cout<<f[td][n]<<endl;
}
return 0;
}
动态规划解循环石子堆合并问题
最新推荐文章于 2023-03-29 16:46:44 发布