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

题目

有 n 个瓶子,它们在桌子上排成一排。第 i 个瓶子的颜色为 ci,每次操作可以选择两个相邻的瓶子,消耗他们颜色的数值乘积的代价将其中一个瓶子的颜色变成另一个瓶子的颜色。
现在要让所以瓶子的颜色都一样,操作次数不限,但要使得操作的总代价最小。

题解

比赛时看错题了,看成了相邻两个合并,最后只剩下一个瓶子…
突破口,最后剩下的瓶子只有一种颜色。
设瓶子最后的颜色为m。
考虑一种似乎可行的方法:尽量让ci=m的不要动。
每个ci不等于m的连续区间,要么直接变成m,要么先变成该区间的最小的颜色,然后再变成m。
灵感:在拍一个数据的时候想到。
2 2 2 1 2 2答案显然为2。
似乎是对的。但是这个反例下就错了。
2 2 2 1e5 1 2 1e5 2 2 2
结果错了。没有举特殊的反例。 惨痛的教训。
正解:
仍然设 f [ i ] [ j ] f[i][j] f[i][j]表示 [ i , j ] [i,j] [i,j]区间内,全部变成该区间内最小的所需要的代价。
直接转移即可。枚举最后一段区间:全部变成该区间内最小的,然后再变成m。
枚举m,顺着做一遍,倒着做一遍即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define LL long long
#define N 305
#define M 100010
using namespace std;
LL g[N][N],f[N][N],c[N],ans,sum,mx,s1,w;
LL F[N],G[N];
int hd[M],nxt[M],b[M],cnt[M];
int i,j,k,l,n,T,len;
int main(){
        scanf("%d",&T);
	while(T--){
		memset(b,0,sizeof(b));
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		memset(F,0,sizeof(F));
		memset(G,0,sizeof(G));
		memset(nxt,0,sizeof(nxt));
		memset(hd,0,sizeof(hd));
		memset(cnt,0,sizeof(cnt));
		scanf("%d",&n);
		fo(i,1,n){
			scanf("%lld",&c[i]);
			g[i][i]=c[i];
			f[i][i]=0;
			if(b[c[i]])nxt[b[c[i]]]=i;
			else hd[c[i]]=i;
			b[c[i]]=i;
			cnt[c[i]]++;
		}
		fo(i,1,n)if(!nxt[i])nxt[i]=n+1;
		fo(i,1,n)fo(j,i+1,n)g[i][j]=min(g[i][j-1],c[j]);
		fo(i,1,n){
			fo(j,i+1,n){
				fo(k,i,j){
					if(g[i][j]!=c[k])f[i][j]=f[i][j]+g[i][j]*c[k];
				}
			}
			f[i][n+1]=f[i][n];
		}
		ans=100000000000000000;
		fo(i,1,100000)if(cnt[i]){
			F[0]=G[n+1]=0;
			fo(j,1,n){
				F[j]=100000000000000000;
				fo(k,0,j-1){
					w=0;
					if(g[k+1][j]!=i)w=g[k+1][j]*i*(j-k);
					F[j]=min(F[j],F[k]+f[k+1][j]+w);
				}
			}
			fd(j,n,1){
				G[j]=100000000000000000;
				fo(k,j,n){
					w=0;
					if(g[j][k]!=i)w=g[j][k]*i*(k-j+1);
					G[j]=min(G[j],G[k+1]+f[j][k]+w);
				}
			}
			j=hd[i];
			while(j^(n+1)){
				ans=min(ans,F[j-1]+G[j+1]);
				j=nxt[j];
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}```
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值