C++石子合并问题(动态规划)

一、问题描述

问题描述:在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选择相邻的两堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。

算法设计:试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。

样例输入:由input.txt提供输入数据

样例输出:输出数据保存到output.txt

输入文件示例:input.txt

4

4 4 5 9

输出文件示例:output.txt

43

54

二、问题分析(以求最大得分为例)

1.先看石子排成一排的问题

扫描二维码关注公众号,回复: 1781945 查看本文章

石子数量如下:

4 4 5 9

求其最大的合并值

m[i][j]=m[i][k]+m[k+1][j]+getsum(i,j);

这里的i,j是指石子的下标,第i堆,第j堆。m[i][j]表示合并第i堆到第j堆的石子的最大得分。其中getsum=a[i]+...+a[j];表示加上合并后总的石子数量。

2.如果石子是按环形排列

环形排列存在的问题:

合并方向不明确的问题,比如m[1][3]

在线性问题中,它表示的是讲a[1],a[2],a[3]三堆石子合并起来,然而在环形排列中我们它存在两种情况。一是:合并a[1],a[2],a[3];二是:合并a[3],a[4],a[1]

解决方案,更改表示方法,比如m[i][j]

它表示的是合并从第i堆开始的往下j堆石子,总共j+1堆。即a[i],a[i+1],a[i+j]

此时又会出现一个问题:下标越界问题

解决方案:对下标取余数。

因此计算公式为

  int t=m[i][k]+m[(i+k)%n+1][j-k-1]+getsum(i,j);

其中getsum的实现代码为:

int getsum(int i,int j,int n){

if(i+j<n) return su[i+j]-su[i-1];

else return (su[n]-su[i-1])+(su[j-(n-i)]);

}

这里的s[i]表示第1堆加到第i堆的石子总数。

(3).数据储存问题

m[i][j]储存合并第i堆到第i+j堆石子的最大得分

特殊情况:m[i][0]=0,当只有一堆石子的时候不需要合并,因此得分为0

(4).回溯路径

在计算得分的时候,用了一个s来储存路径。

s[i][j]=k;表示第i堆到第i+j堆石子最大得分的划分方法为先合并第i堆到第i+k堆,在合并第i+k+1堆到第j堆。

代码如下:

void Traceback(int **s,int i,int j,int n,ofstream &outfile){

if(j==0) return;

Traceback(s,i,s[i][j],n,outfile);

Traceback(s,s[i][j]%n+1,j-s[i][j]-1,n,outfile);

outfile<<"Add A"<<i<<","<<(i+s[i][j]-1)%n+1;

outfile<<" and A"<<(i+s[i][j])%n+1<<","<<(i+j-1)%n+1<<endl;

}

(5).问题细分情况说明

为何在m[i][n-1]的值会不一样?1 2 3 4为例

就是因为它们各自代表了的问题的一种情况;

比如m[1][3],它的划分情况有:m[1][2]+m[4][0],m[1][1]+m[3][1],m[1][0]+m[2][2]

在这种划分情况下,只有14有可能最后才被合并。

     m[2][3],它的划分情况有:m[2][2]+m[1][0],m[2][1]+m[4][1],m[2][0]+m[3][2]

而在这种划分情况下,只有21有可能最后才被合并。


从选定的位置开始划分

三、详细设计(从算法到程序)

#include<iostream>
#include<fstream>
#include<sstream>
using namespace std;
int su[256];

int getsum(int i,int j,int n){
	if(i+j<n) return su[i+j]-su[i-1];
	else return (su[n]-su[i-1])+(su[j-(n-i)]);
}
      

int DeMin(int *a,int n,int **m,int **s)
{
	for(int i=1;i<=n;i++) m[i][0]=0;	
	for(int j=1;j<=n-1;j++)
	  for(int i=1;i<=n;i++){//i,j互换位置,按照j=0,j=1的顺序对矩阵进行填值 
	  	int k=j-1;
		m[i][j]=m[i][k]+m[(i+k)%n+1][0]+getsum(i,j,n);
		s[i][j]=k;
	  	for(k=j-2;k>=0;k--){
	  		int t=m[i][k]+m[(i+k)%n+1][j-k-1]+getsum(i,j,n);
	  		if(t<m[i][j]){m[i][j]=t;s[i][j]=k;}
		}
	  }
}

int DeMax(int *a,int n,int **m,int **s)
{
	for(int i=1;i<=n;i++) m[i][0]=0;//只有一堆石子 
	for(int j=1;j<=n-1;j++)
	  for(int i=1;i<=n;i++){//i,j互换位置,按照j=0,j=1的顺序对矩阵进行填值 
	  	int k=j-1;
		m[i][j]=m[i][k]+m[(i+k)%n+1][0]+getsum(i,j,n);
		s[i][j]=k;
	  	for(k=j-2;k>=0;k--){
	  		int t=m[i][k]+m[(i+k)%n+1][j-k-1]+getsum(i,k,n);
	  		if(t>m[i][j]){m[i][j]=t;s[i][j]=k;}
		}
	  }
} 

void Traceback(int **s,int i,int j,int n,ofstream &outfile){
	if(j==0) return;
	Traceback(s,i,s[i][j],n,outfile);
	Traceback(s,s[i][j]%n+1,j-s[i][j]-1,n,outfile);
	outfile<<"Add A"<<i<<","<<(i+s[i][j]-1)%n+1;
	outfile<<" and A"<<(i+s[i][j])%n+1<<","<<(i+j-1)%n+1<<endl;
}

int main(){  
    ifstream cinfile;
    cinfile.open("input.txt",ios::in);
    int n;              
	cinfile>>n;
	int Stonum[n+1];
	Stonum[0]=0;
	for(int i=1;i<=n;i++) cinfile>>Stonum[i];
	cinfile.close();
	
	int **SN=new int*[n+1]();
	int **TN=new int*[n+1]();
	for(int i=0;i<=n;i++){
		SN[i]=new int[n+1];
		TN[i]=new int[n+1]; 
	}
	for(int i=0;i<=n;i++)
	  for(int j=0;j<=n;j++){
	  	SN[i][j]=0;
	  	TN[i][j]=0; 
	  } 
	su[0]=0;
	for(int i=1;i<=n;i++) su[i]=su[i-1]+Stonum[i];
	
	ofstream outfile;
	outfile.open("output.txt",ios::out);
	DeMin(Stonum,n,SN,TN);
	int Min=SN[1][n-1],posimin=1;
	for(int i=2;i<=n;i++){
		if(Min>SN[i][n-1]){
		  Min=SN[i][n-1];
		  posimin=i;
	    }
	}
	outfile<<Min<<endl;
	outfile<<"具体合并方案:"<<endl; 
	Traceback(TN,posimin,n-1,n,outfile);
	
	DeMax(Stonum,n,SN,TN);
	int Max=SN[1][n-1],posimax=1;
	for(int i=2;i<=n;i++){
		if(Max<SN[i][n-1]){
		  Max=SN[i][n-1];
		  posimax=i;
	    }
	}
	outfile<<Max<<endl;
	outfile<<"具体合并方案:"<<endl;
	Traceback(TN,posimax,n-1,n,outfile);

}



四、程序运行结果

 

五、分析与总结

在计算m[i][j]时应该按列进行。

for(int j=2;j<=n-1;j++)

  for(int i=1;i<=n;i++){//i,j互换位置,按照j=0,j=1的顺序对矩阵进行填值

}

   

   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/alexwym/article/details/80798845
今日推荐