jzoj5925 naive 的瓶子 贪心

33 篇文章 0 订阅

Description


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

Solution


只要敢写就能A系列。。
一个比较显然的做法:
我们设f[i,j,k]为i到j变为k的最小代价,然后可以结合暴力得到60分

考虑贪心。我们枚举最终的数字c,把它们的位置抠出来。
对于相邻两个目标数字之间的数有两种方法:

  1. 直接变成c
  2. 先变成区间最小值,再变成c

我们两种都算一下取min即可。

实际上还可以随便找一种方案,记此时不一定最优秀的答案为ans’,我们可以用ans’来作为暴力搜索的剪枝。这样也可以跑过
实际上有位老哥钦定全部数字变成最小值,这样就能获得95分的高分(滑稽

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <map>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

typedef long long LL;
const int N=305;

std:: map <int,int> rec;
std:: vector <int> vec[N];

LL min[N][N],c[N],s[N];
LL lxf[N][N];
int n,tot;

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void init() {
	fill(min,0); fill(lxf,0);
	rec.clear();
	n=read(),tot=0;
	rep(i,1,n) {
		c[i]=read();
		if (!rec[c[i]]) {
			rec[c[i]]=++tot;
			vec[tot].clear();
		}
		vec[rec[c[i]]].push_back(i);
		s[i]=s[i-1]+c[i];
	}
	rep(i,1,tot) vec[i].push_back(n+1);
	rep(i,1,n) {
		min[i][i]=c[i];
		rep(j,i+1,n) min[i][j]=std:: min(min[i][j-1],c[j]);
	}
	rep(i,1,n) {
		lxf[i][rec[c[i]]]=c[i];
		rep(j,1,tot) lxf[i][j]+=lxf[i-1][j];
	}
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	for (int T=read();T--;) {
		init(); LL ans=1e15;
		rep(i,1,n) {
			int col=c[i],pre=0; LL wjp=0;
			for (int j=0;j<vec[rec[col]].size();++j) {
				int now=vec[rec[col]][j];
				LL sum=s[now-1]-s[pre],mn=min[pre+1][now-1];
				int tmp=rec[mn];
				wjp+=std:: min(sum*col,(sum-(lxf[now-1][tmp]-lxf[pre][tmp]))*mn+mn*col*(now-pre-1));
				pre=vec[rec[col]][j];
			}
			ans=std:: min(ans,wjp);
		}
		printf("%lld\n", ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值