C/C++ 用递归(分治法)解决多米诺骨牌问题

 问题现有 n 块“多米诺骨牌” s1, s2, · · · , sn 水平放成一排,每块骨 牌 si 包含左右两个部分,每个部分赋予一个非负整数值,骨牌可做 180 度旋转,使得原来在左边的值变到右边,而原来 在右边的值移到左边,假设不论 si 如何旋转,L[i] 总是存储 si 左边的值,R[i] 总是存储 si 右边的值,status[i] 用于存储 si 的状态:当 L[i] R[i] 时记为 0,否 则记为 1,试采用分治法设计算法

    求 \sum_{i=1}^{n-1}=R[i ] · L [ i + 1] 的最大值,以及当取 得最大值时每个骨牌的状态。

 

条件分析:
1:每块骨牌有左右两部分都为非负整数值
2:左右部分的值可调换
3:调换后,存储状态status改变
4:前一骨牌的右部分乘以后一骨牌的左部分(第一个的左部分和最后一个的右部分不参与计算)
4:用分治法,即将大问题转换为小问题
关键代码:
 Status Recursion(int n ,Data L)//递归操作
{
	 if (n == 0)return ERROR;
	 Recursion(n-1, L);
	 int _left = Sum.Left;//记录第1个骨牌到第n个骨牌的左部分为止的最大值
	 if (L[n].left * L[n - 1].left+Sum.Right < L[n].left * L[n - 1].right+Sum.Left)//记录从第1个骨牌到第n+1个骨牌的左部分为止的最大值
	 {
		 Sum.Left+= L[n].left * L[n - 1].right;
	 }
	 else
	 {
		 Sum.Left = L[n].left * L[n - 1].left+ Sum.Right;
		 if (L[n-1].status[0])//由于第n个骨牌调换了位置,所以状态要改变
		 {
			 L[n-1].status[0] = 0;
		 }
		 else
		 {
			 L[n-1].status[0] = 1;
		 }
	 }
	 if (L[n].right * L[n - 1].left+Sum.Right < L[n].right * L[n - 1].right+ _left)//记录从第1个骨牌到第n+1个骨牌的右部分为止的最大值
	 {
		 Sum.Right = L[n].right * L[n - 1].right+ _left;
	 }
	 else
	 {
		 Sum.Right = L[n].right * L[n - 1].left + Sum.Right;
		 if (L[n-1].status[1])//由于第n个骨牌调换了位置,所以状态要改变
		 {
			 L[n-1].status[1] = 0;
		 }
		 else
		 {
			 L[n-1].status[1] = 1;
		 }
	 }
	 return OK;
}

总体实现:

函数定义

#include<iostream>
#define OK 1;
#define ERROR 0;
const int LENGTH =6;//骨牌的数目
typedef int Status;
typedef struct _data
{
	int left;//骨牌左边值
	int right;//骨牌右边值
	int status[2];//骨牌的状态
}Data[LENGTH];
typedef struct _Sum
{
	int Left;
	int Right;
}_Sum;
static _Sum Sum = {0,0};
static int Num = LENGTH;
void Init(Data & L)//初始化骨牌
{
	int i;
	for (i = 0; i < LENGTH; i++)
	{
		scanf_s("%d", &L[i].left);
		scanf_s("%d", &L[i].right);
		if (L[i].left <= L[i].right)//初始时将骨牌状态确定
		{
			L[i].status[0] = 0;
			L[i].status[1] = 0;
		}
		else
		{
			L[i].status[0] = 1;
			L[i].status[1] = 1;
		}
	}
}
 Status Recursion(int n ,Data L)//递归操作
{
	 if (n == 0)return ERROR;
	 Recursion(n-1, L);
	 int _left = Sum.Left;//记录第1个骨牌到第n个骨牌的左部分为止的最大值
	 if (L[n].left * L[n - 1].left+Sum.Right < L[n].left * L[n - 1].right+Sum.Left)//记录从第1个骨牌到第n+1个骨牌的左部分为止的最大值
	 {
		 Sum.Left+= L[n].left * L[n - 1].right;
	 }
	 else
	 {
		 Sum.Left = L[n].left * L[n - 1].left+ Sum.Right;
		 if (L[n-1].status[0])//由于第n个骨牌调换了位置,所以状态要改变
		 {
			 L[n-1].status[0] = 0;
		 }
		 else
		 {
			 L[n-1].status[0] = 1;
		 }
	 }
	 if (L[n].right * L[n - 1].left+Sum.Right < L[n].right * L[n - 1].right+ _left)//记录从第1个骨牌到第n+1个骨牌的右部分为止的最大值
	 {
		 Sum.Right = L[n].right * L[n - 1].right+ _left;
	 }
	 else
	 {
		 Sum.Right = L[n].right * L[n - 1].left + Sum.Right;
		 if (L[n-1].status[1])//由于第n个骨牌调换了位置,所以状态要改变
		 {
			 L[n-1].status[1] = 0;
		 }
		 else
		 {
			 L[n-1].status[1] = 1;
		 }
	 }
	 return OK;
}

主函数:

int main()
{	
	int i;
	Data _data;
	Init(_data);//初始化
	Recursion(Num-1, _data);//递归操作
	if (Sum.Left > Sum.Right)
	{
		printf("%d ", Sum.Left);
		for (i = 0; i < Num; i++)
		{
			printf("%d ", _data[i].status[0]);
		}
     }
	else//最后一个骨牌调换位置,状态改变
	{
		printf("%d ", Sum.Right);
		if (_data[Num-1].status[1])
		{
			_data[Num - 1].status[1] = 0;
		}
		else
		{
			_data[Num - 1].status[1] = 1;
		}
		for (i = 0; i < Num; i++)
		{
			printf("%d ", _data[i].status[1]);
		}
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值