JZOJ-senior-5925. 【NOIP2018模擬10.25】naive 的瓶子
阿新 • • 發佈:2018-12-17
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了 設 表示 中的最小數 表示將 全部染成 的代價 為把前 個瓶子染成目標顏色的最小代價 每次轉移選擇接下來的一個區間,先把他們染成這個區間中數值最小的顏色,再染成目標顏色即可
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();
}