计蒜客卡牌游戏

问题描述:

蒜头君手里有 n 张卡牌,编号从 1 到 n,每张卡牌上面有一个数字 num  i ​  。现在蒜头君将 n 张卡牌排成一行,组成一个序列,执行以下操作:从序列中抽取一张编号为 i 的卡牌,则该张卡牌贡献的得分为 num  i−1 ​  ×num  i ​  ×num  i+1 ​  ,即卡牌上的数字同左右两张相邻的卡牌上的数字乘积。但是不能抽取序列中最左边和最右边的卡牌,即  i≠1 且 i≠n。抽到的卡牌就从序列中去掉。重复上述操作,直到序列里只剩两张卡牌。抽取的总得分为每次抽取的得分之和。  现在蒜头君想知道,怎么进行卡牌抽取,可以使得总得分最小。

输入格式:

输入有两行。第一行输入一个整数 n(3≤n≤100),表示一共有 n 张卡牌。  第二行输入 n 个整数 num  i (1≤num  i ​  ≤100),表示 n 张卡牌上面的数字。

输出格式:

输出一行,输出一个整数,表示卡牌抽取的最小总得分。

样例输入:

5 20 30 5 18 3 

样例输出:2520

思路:

dp[i][j]表示从将第i张到第j张牌都抽出来的最小值。若要保证dp[i][j]值不变,则需保证a[i-1]与a[j+1]值不变。假设在i到j其中最后抽出第k张牌,则抽出的价值为w[k]=a[i-1]*a[k]*a[j+1],而前面和后面区间抽出的总价值不变。所以dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+w[k])。

代码:

#include
using namespace std;
const int N=110;
int n,a[N],w[N],dp[N][N];
int main(){
	freopen("data.in","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) 
		scanf("%d",&a[i]);
	for(int i=2;i<=n-1;i++)
		dp[i][i]=a[i-1]*a[i]*a[i+1];
	for(int i=2;i<=n-2;i++)
		dp[i][i+1]=min(dp[i][i]+a[i-1]*a[i+1]*a[i+2],dp[i+1][i+1]+a[i-1]*a[i]*a[i+2]);
	for(int l=3;l<=n-2;l++)
		for(int i=2,j;i+l-1<=n-1;i++){
			j=i+l-1;
			dp[i][j]=min(a[i-1]*a[i]*a[j+1]+dp[i+1][j],a[i-1]*a[j]*a[j+1]+dp[i][j-1]);
			for(int k=i+1;k<=j-1;k++)
				dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+a[i-1]*a[k]*a[j+1]);
		}
	printf("%d",dp[2][n-1]);
	return 0;
}

你可能感兴趣的:(计蒜客卡牌游戏)