动态规划解循环石子堆合并问题

 
#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;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值