Description
众所周知,小 naive 有 n 个瓶子,它们在桌子上排成一排。第 i 个瓶子的颜色为 ci,每个瓶子都有灵性,每次操作可以选择两个相邻的瓶子,消耗他们颜色的数值乘积的代价将其中一个瓶子的颜色变成另一个瓶子的颜色。
现在 naive 要让所以瓶子的颜色都一样,操作次数不限,但要使得操作的总代价最小。
Solution
只要敢写就能A系列。。
一个比较显然的做法:
我们设f[i,j,k]为i到j变为k的最小代价,然后可以结合暴力得到60分
考虑贪心。我们枚举最终的数字c,把它们的位置抠出来。
对于相邻两个目标数字之间的数有两种方法:
- 直接变成c
- 先变成区间最小值,再变成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;
}