2018.10.26【校内模拟】瓶子(DP)

传送门


解析:

首先,这道题用贪心+面向数据编程的做法水过去的有点多啊,不过没办法,谁叫数据那么弱呢。。。。

思路:

一看 n n n非常小,只有300,顿时明白可以乱搞, O ( n ) O(n) O(n)枚举每一个瓶子的颜色作为最终颜色,直接 O ( n 2 ) D P O(n^2)DP O(n2)DP求出 f i f_i fi表示将前 i i i个全部涂成当前目标颜色的最小代价, g i g_i gi表示将后面 i − n i-n in全部涂成目标颜色的最小代价,显然这个求出来后拼接一下就是答案。

那么考虑DP的转移,我们每次考虑将一段区间涂成目标颜色,显然一个瓶子被涂色的方案只有可能是一下两种:
1.直接被涂成目标颜色
2.先被涂成一个权值较小的颜色,再涂成目标颜色

可以证明,这个权值较小的颜色最优情况下一定是区间最小值,用 S T ST ST表求一下就行了。

而且每个瓶子被漆成权值较小的颜色这个过程显然只会执行一次,如果两次漆成了其他颜色,显然是不够优的。

那么状态瞎转移一下,就讨论这两种情况就行了。


代码(首先,博主知道自己的宏写的很丑,不过并不想写函数,您可以假装这是一个锻炼自己代码阅读能力的机会 )(而且DP的转移有一点点问题,不过数据太弱了,懒得改了):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=302;
cs int INF=0x3f3f3f3f;
int logn[N];
int minn[N][10];
int col[N],a[N],sum[N];
int n;
ll f[N],g[N];
ll ans;

#define s(l,r) (sum[(r)]-sum[(l)-1])
#define Min(l,r) min(minn[(l)][logn[(r)-(l)+1]],minn[(r)-(1<<logn[(r)-(l)+1])+1][logn[(r)-(l)+1]])

int T;
signed main(){
	for(int re i=2;i<=300;++i)logn[i]=logn[i>>1]+1;
	T=getint();
	while(T--){
		n=getint();
		for(int re i=1;i<=n;++i)
		sum[i]=sum[i-1]+(minn[i][0]=col[i]=getint());
		for(int re i=1;i<=logn[n];++i)
		for(int re j=1;j+(1<<i)-1<=n;++j)
		minn[j][i]=min(minn[j][i-1],minn[j+(1<<i-1)][i-1]);
		
		ans=1ll*INF*INF;
		for(int re k=1;k<=n;++k){
			
			memset(f,INF,sizeof f);
			f[k]=min(1ll*col[k]*sum[k-1],1ll*sum[k-1]*Min(1,k)+1ll*col[k]*Min(1,k)*(k-1));
			for(int re i=k+1;i<=n;++i){
				if(col[i]==col[k]){
					f[i]=f[i-1];
					continue;
				}
				for(int re j=i-1;j>=k;--j){
					f[i]=min(f[i],f[j]+1ll*s(j+1,i)*col[k]);
					f[i]=min(f[i],f[j]+1ll*Min(j+1,i)*s(j+1,i)+1ll*col[k]*Min(j+1,i)*(i-j));
				}
			}
			
			int last=n;
			while(col[last]!=col[k])--last;
			memset(g,INF,sizeof g);
			g[last]=min(1ll*col[k]*s(last+1,n),1ll*s(last+1,n)*Min(last,n)+1ll*col[last]*Min(last,n)*(n-last));
			for(int re i=last-1;i;--i){
				if(col[i]==col[last]){
					g[i]=g[i+1];
					continue;
				}
				for(int re j=i+1;j<=last;++j){
					g[i]=min(g[i],g[j]+1ll*s(i,j-1)*col[last]);
					g[i]=min(g[i],g[j]+1ll*Min(i,j-1)*s(i,j-1)+1ll*col[last]*Min(i,j-1)*(j-i));
				}
			}
			f[0]=g[n+1]=0;
			for(int re i=0;i<=n;++i)ans=min(ans,f[i]+g[i+1]);
		}
		cout<<ans<<"\n";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值