JZOJ-senior-5925. 【NOIP2018模拟10.25】naive 的瓶子

4 篇文章 0 订阅
Time Limits: 2000 ms Memory Limits: 524288 KB

Description

众所周知,小 naive 有 n 个瓶子,它们在桌子上排成一排。第 i 个瓶子的颜色为 ci,每个瓶子都有灵性,每次操作可以选择两个相邻的瓶子,消耗他们颜色的数值乘积的代价将其中一个瓶子的颜色变成另一个瓶子的颜色。

现在 naive 要让所以瓶子的颜色都一样,操作次数不限,但要使得操作的总代价最小。

Input

输入文件为 colour.in
一个测试点内多组数据。
第一行,一个正整数 T,表示数据组数。
每组数据内:
第一行一个整数 n,为瓶子的个数。
第二行共 n 个整数,第 i 个整数为第 i 个瓶子的颜色 ci。

Output

输入文件为 colour.out。
共一行,一个整数,为最小的总代价。

Sample Input

4
7 4 6 10

Sample Output

92

样例解释
{7 4 6 10}− > {4 4 6 10}− > {4 4 4 10}− > {4 4 4 4}。
总代价为 7 × 4 + 4 × 6 + 4 × 10 = 92。

Data Constraint

1 ≤ T ≤ 10。
对于测试点内的每组数据:
在这里插入图片描述

Solution

早上比赛发现了一个神奇的结论:瓶子要么直接被染成目标颜色,要么被染成一个较小的数值,再被染成目标颜色,然而我还是只有暴力分,见DP死选手……

首先,我们可以枚举最后的颜色,我们肯定是把一些颜色不为目标颜色的数值比较大的瓶子染成数值比较小颜色,但要确保还存在目标颜色,再最后把所有其他颜色染成目标颜色。在第一步中,一个瓶子颜色的数值肯定不会变大,此外,也不会变小两次,假设他变小了两次,那么我们完全可以直接变小成第二次,这样肯定不会变劣。

接下来,我们就可以DP了
h [ l ] [ r ] h[l][r] h[l][r] 表示 [ l , r ] [l,r] [l,r] 中的最小数
g [ l ] [ r ] g[l][r] g[l][r] 表示将 [ l , r ] [l,r] [l,r] 全部染成 h [ l ] [ r ] h[l][r] h[l][r] 的代价
f [ i ] f[i] f[i] 为把前 i i i 个瓶子染成目标颜色的最小代价
每次转移选择接下来的一个区间,先把他们染成这个区间中数值最小的颜色,再染成目标颜色即可

Code

#include<algorithm>
#include<cstring>
#include<cstdio>

#define fo(i,a,b) for(ll i=a;i<=b;++i)
#define fd(i,a,b) for(ll i=a;i>=b;--i)
#define ll long long

using namespace std;

const ll N=310;
const ll MX=1e18;
ll T,n,a[N],b[N];
ll f[N],g[N][N],h[N][N];

void solve()
{
	scanf("%lld",&n);
	memset(h,127,sizeof(h));
	fo(i,1,n)
	{
		scanf("%lld",&a[i]);
		h[i][i]=b[i]=a[i];
	}
	sort(b+1,b+1+n);
	ll cnt=unique(b+1,b+1+n)-b-1;
	fd(l,n,1)
		fo(r,l+1,n)
		{
			h[l][r]=min(h[l+1][r],h[l][r-1]);
			ll mn=a[l],gs=1,s=a[l];
			fo(i,l+1,r)
			{
				s+=a[i];
				if(a[i]<mn) mn=a[i],gs=1;
					else if(a[i]==mn) ++gs;
			}
			g[l][r]=(s-mn*gs)*mn;
		}
	ll ans=MX;
	fo(i,1,cnt)
	{
		memset(f,127,sizeof(f));
		if(a[1]==b[i]) f[1]=0; else f[1]=a[1]*b[i];
		fo(en,2,n)
			fo(r,1,en-1)
			{
				if(h[r+1][en]==b[i]) f[en]=min(f[en],f[r]+g[r+1][en]);
				else f[en]=min(f[en],f[r]+g[r+1][en]+h[r+1][en]*b[i]*(en-r));
			}
		ans=min(ans,f[n]);
	}
	printf("%lld\n",ans);
}

int main()
{
	freopen("colour.in","r",stdin);
	freopen("colour.out","w",stdout);
	scanf("%lld",&T);
	while(T--) solve();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值