1. 程式人生 > >JZOJ5925. 【NOIP2018模擬10.25】naive 的瓶子

JZOJ5925. 【NOIP2018模擬10.25】naive 的瓶子

Description

眾所周知,小 naive 有 n (n≤300)個瓶子,它們在桌子上排成一排。第 i 個瓶子的顏色為 ci,每個瓶子都有靈性,每次操作可以選擇兩個相鄰的瓶子,消耗他們顏色的數值乘積的代價將其中一個瓶子的顏色變成另一個瓶子的顏色。
現在 naive 要讓所以瓶子的顏色都一樣,操作次數不限,但要使得操作的總代價最小。

題解

顏色的數量很少,可以考慮列舉最後變成哪一種顏色。
對於一個瓶子,可以發現它只有兩種可能,
要麼直接變成目標顏色,
要麼先變為一個較小的顏色,然後再變成目標顏色。
於是就可以dp了,
f

i f_i 表示前i個瓶子,都變成了目標顏色的最小代價。
轉移的時候列舉一個j,表示區間[j,i]這些瓶子都是先變為區間[j,i]的最小值,然後再變為目標顏色。
同理維護一個字尾的g。
答案就是 f i
1 + g i + 1 f_{i-1}+g_{i+1}

code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define ll long long
#define N 303
#define P putchar
#define G getchar
using namespace std;
char ch;
void read(int &n)
{
	n=0;
	ch=G();
	while((ch<'0' || ch>'9') && ch!='-')ch=G();
	int w=1;
	if(ch=='-')w=-1,ch=G();
	while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
	n*=w;
}

ll min(ll a,ll b){return a<b?a:b;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

int n,T,a[N];
ll f[N],g[N],sum,mi,s,ans;

int main()
{
	freopen("colour.in","r",stdin);
	freopen("colour.out","w",stdout);
	
	for(read(T);T;T--)
	{
		read(n);
		for(int i=1;i<=n;i++)read(a[i]);
		ans=9223372036854775807;
		for(int col=1;col<=n;col++)
		{
			f[0]=g[n+1]=0;
			for(int i=1;i<=n;i++)
			{
				f[i]=mi=9223372036854775807;sum=s=0;
				for(int j=i;j;j--)
				{
					if(mi>a[j])mi=a[j],s=1;else
					if(mi==a[j])s++;
					sum=sum+a[j];
					f[i]=min(f[i],f[j-1]+(sum-s*mi)*mi+((mi^a[col])?mi*a[col]*(i-j+1):0));
				}
			}
			
			for(int i=n;i;i--)
			{
				g[i]=mi=9223372036854775807;sum=s=0;
				for(int j=i;j<=n;j++)
				{
					if(mi>a[j])mi=a[j],s=1;else
					if(mi==a[j])s++;
					sum=sum+a[j];
					g[i]=min(g[i],g[j+1]+(sum-s*mi)*mi+((mi^a[col])?mi*a[col]*(j-i+1):0));
				}
			}
			ans=min(ans,f[col-1]+g[col+1]);
		}
		write(ans);P('\n');
	}
	
	return 0;
}