石子合并问题(环形)

你看我打不打你
本文链接: https://blog.csdn.net/KO812605128/article/details/101196345

石子合并问题
Time Limit: 1000 ms Memory Limit: 65536 KiB

Problem Description
在一个 圆形 操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。
对于给定n堆石子,计算合并成一堆的最小得分和最大得分。
Input
输入数据的第1行是正整数n,1≤n≤100,表示有n堆石子。第二行有n个数,分别表示每堆石子的个数。
Output
输出数据有两行,第1行中的数是最小得分,第2行中的数是最大得分。
Sample Input
4
4 4 5 9
Sample Output
43
54
接下介绍实例分析
最小值:

4 4 5 9 score: 0
8 5 9 score: 8
13 9 score: 8 + 13 = 21
22 score: 8 + 13 + 22 = 43

最大值:

4 4 5 9 score: 0
4 4 14 score: 14
4 18 score: 4 + 18 = 22
22 score: 14 + 18 + 22 = 54

参考博客原文:讲解很好

分析

假设有石头Ai,Ai+1,……,Ai+j-1共j堆需要合并,简记为A[i+0,i+j-1].如果设最后一次合并发生在Ak与Ak+1之间(i<=k <=i+j-1),则最后一个合并的得分为
Ai,Ai+1,……,Ai+j-1堆石头的个数的总和记为totalValue(i,j).(不管你最后一次合并发生在哪个位置,totalValue(i,j)的值都是一样的)因此总的得分等于

A[i,k]+A[k+1,i+j-1]+totalValue(i,j).
在这里插入图片描述
动态规划思路:

阶段i:石子的每一次合并过程,先两两合并,再三三合并,…最后N堆合并 
状态s:每一阶段中各个不同合并方法的石子合并总得分。 
决策:把当前阶段的合并方法细分成前一阶段已计算出的方法,选择其中的最优方案 
具体来说我们应该定义一个数组s[i,j]用来表示合并方法,s[i][j]表示从第i堆开始数j堆进行合并,s[i,j]为合并的最优得分。
 对例子(3 4 6 5 4 2)来说:
 第一阶段:s[1,1]=0,s[2,1]=0,s[3,1]=0,s[4,1]=0,s[5,1]=0,s[6,1]=0,因为一开始还没有合并,所以这些值应该全部为0。
 第二阶段:两两合并过程如下,其中sum(i,j)表示从i开始数j个数的和
           s[1,2]=s[1,1]+s[2,1]+sum(1,2)
           s[2,2]=s[2,1]+s[3,1]+sum(2,2)
           s[3,2]=s[3,1]+s[4,1]+sum(3,2)
           s[4,2]=s[4,1]+s[5,1]+sum(4,2)
           s[5,2]=s[5,1]+s[6,1]+sum(5,2)
           s[6,2]=s[6,1]+s[1,1]+sum(6,2)
 第三阶段:三三合并可以拆成两两合并,拆分方法有两种,前两个为一组或后两个为一组
      s[1,3]=s[1,2]+s[3,1]+sum(1,3)或s[1,3]=s[1,1]+s[2,2]+sum(1,3),取其最优(最大或最小)
      s[2,3]=s[2,2]+s[4,1]+sum(2,3)或s[1,3]=s[2,1]+s[3,2]+sum(2,3),取其最优                                                                      
 第四阶段:四四合并的拆分方法用三种,同理求出三种分法的得分,取其最优即可。以后第五阶段、第六阶段依次类推,
 最后在第六阶段中找出最优答案即可。

AC代码:


#include<bits/stdc++.h>

using namespace std;

const int Max = 99999;
const int Min = 0;

int n, a[200];///n表示堆数,a表示每队的数量
int dp[200][200];///dp[i][j]表示从第i堆开始合并j堆(包括第i堆)后的最小花费
int dq[200][200];///dq表示最大

int sum(int i, int t)///从第i堆开始,t个石堆合,最后一次的合并的花费
{
    int s = 0, k1, k;
    for(k = i; k < i + t; k++)
    {
        k1 = k % n;
        if(k1 == 0) k1 = n;
        s = s + a[k1];///这t个石堆最好两堆合并时的花费一定是这t堆石子的总和
    }
    return s;
}

int main()
{
    cin>>n;
    for(int i = 1; i <= n; i++)
    {
        cin>>a[i];
        dp[i][1] = 0;///表示合并一堆的花费,没有合并则花费为0
        dq[i][1] = 0;
    }
    ///动态规划
    for(int t = 2; t <= n; t++)
    {
        for(int i = 1; i <= n; i++)
        {
            dp[i][t] = Max;
            dq[i][t] = Min;
            for(int k = 1; k < t; k++)
            {
                dp[i][t] = min(dp[i][t], dp[i][k] + dp[(i + k - 1) % n + 1][t - k] + sum(i, t));
                dq[i][t] = max(dq[i][t], dq[i][k] + dq[(i + k - 1) % n + 1][t - k] + sum(i, t));
            }
        }
    }
    int mmin = Max;
    int mmax = Min;
    for(int i = 1; i <= n; i++)///从第几堆石子开始结果最小
    {
        mmin = min(mmin, dp[i][n]);
        mmax = max(mmax, dq[i][n]);
    }
    cout<<mmin<<endl;
    cout<<mmax<<endl;
}

测试数据代码,辅助理解,以及容易出现错误的地方

#include<bits/stdc++.h>

using namespace std;

const int Max = 99999;
const int Min = 0;

int n, a[200];///n表示堆数,a表示每队的数量
int dp[200][200];///dp[i][j]表示从第i堆开始合并j堆(包括第i堆)后的最小花费
int dq[200][200];///dq表示最大

int sum(int i, int t)///从第i堆开始,t个石堆合,最后一次的合并的花费
{
    int s = 0, k1, k;
    for(k = i; k < i + t; k++)
    {
        k1 = k % n;///作为数组的下标
        if(k1 == 0) k1 = n;
        s = s + a[k1];///这t个石堆最好两堆合并时的花费一定是这t堆石子的总和
    }
    return s;
}

int main()
{
    int i, k, j;
    cin>>n;
    for(i = 1; i <= n; i++)
    {
        cin>>a[i];
        dp[i][1] = 0;///表示合并一堆的花费,没有合并则花费为0
        //dq[i][1] = 0;
    }
    ///动态规划
    for(i = 2; i <= n; i++)
    {
        for(j = 1; j <= n; j++)
        {
            dp[j][i] = Max;///求最大值把默认值初始化为最小值,反之
            //dq[j][i] = Min;
            for(k = 1; k < i; k++)
            {
                cout<<"dp["<<j<<"]["<<i<<"]="<<dp[j][i]<<"----"<<"dp["<<j<<"]["<<i<<"]="<<dp[j][k]<<"---(j+k-1)%n+1="<<(j + k - 1) % n + 1<<"---sum="<<sum(j, i)<<endl;
                dp[j][i] = min(dp[j][i], dp[j][k] + dp[(j + k - 1) % n + 1][i - k] + sum(j, i));
                //dq[j][i] = max(dq[j][i], dq[j][k] + dq[(j + k - 1) % n + 1][i - k] + sum(j, i));
                cout<<"dp["<<j<<"]["<<i<<"]="<<dp[j][i]<<"---dp[j][k]+dp[(j+k-1)%n+1][i-k]+sum(j, i)="<<dp[j][k] + dp[(j + k - 1) % n + 1][i - k] + sum(j, i)<<endl;
                cout<<"KKKKK结束"<<endl;
                cout<<endl;
            }
            cout<<"JJJJJ结束"<<endl;
            cout<<endl;
        }
        cout<<"IIIII结束"<<endl;
        cout<<endl;
    }
    int mmin = Max;
    int mmax = Min;
    for(int i = 1; i <= n; i++)///从第几堆石子开始结果最小
    {
        mmin = min(mmin, dp[i][n]);
        //mmax = max(mmax, dq[i][n]);
    }
    cout<<mmin<<endl;
    //cout<<mmax<<endl;
}

此代码摘自芙芙个憨憨

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxx = 0x3f3f3f3f;

int main() 
{
	int n;
	cin >> n;
	int stone[210];
	int dp1[210][210];
	int dp2[210][210];
	int sum[210];
	int a1 = maxx;
	int a2 = 0;
	for (int i = 0; i <= 2 * n; i++)
	{
		for (int j = 0; j <= 2 * n; j++)
		{
			dp1[i][j] = maxx;
			dp2[i][j] = -1;
		}
	}
	for (int i = 1; i <= n; i++)
	{
		cin >> stone[i];
		stone[i + n] = stone[i];
		dp1[i][i] = dp2[i][i] = dp1[i + n][i + n] = dp2[i + n][i + n] = 0;
	}
	sum[0] = 0;
	for (int i = 1; i <= 2 * n - 1; i++)
	{
		sum[i] = stone[i] + sum[i - 1];
	}
	for (int len = 1; len <= n - 1; len++)
	{
		for (int i = 1; i <= 2 * n - 1; i++)
		{
			int j = i + len;
			if (j >= (2 * n)) break;
			int k;
			if (len == 1) dp1[i][j] = dp2[i][j] = sum[j] - sum[i-1];
			else
			{
				for (k = i; k < j; k++)
				{
					dp1[i][j] = min(dp1[i][j], dp1[i][k] + dp1[k + 1][j] + sum[j] - sum[i - 1]);
					dp2[i][j] = max(dp2[i][j], dp2[i][k] + dp2[k + 1][j] + sum[j] - sum[i - 1]);
				}
			}
			
		}
	}
	for (int i = 1; i <= n; i++)
	{
		a1 = min(a1, dp1[i][i + n - 1]);
		a2 = max(a2, dp2[i][i + n - 1]);
	}
	cout << a1 << "\n" << a2 << "\n";
	return 0;
}

猜你喜欢

转载自blog.csdn.net/KO812605128/article/details/101196345
今日推荐