第十四届蓝桥杯JavaB组省赛石子合并

0合并石子 - 蓝桥云课 (lanqiao.cn)

典型的区间 d p dp dp,不过和以往不同的是多了只能相同颜色之间合并,并且要求出最少堆数和最小费用。

那我们自然而然的可以把定义: f i , j , k f_{i,j,k} fi,j,k表示的是合并 [ i , j ] [i,j] [i,j]颜色为 k k k的最小费用。此时要注意的每次合并都会让颜色变化。所以我们的突破口就是颜色,并且 c n t i , j cnt_{i,j} cnti,j表示的是区间 [ i , j ] [i,j] [i,j]最少有几堆石头, w i , j w_{i,j} wi,j表示区间 [ i , j ] [i,j] [i,j]的最小费用。

假设: f i , j , k = i n f f_{i,j,k}=inf fi,j,k=inf表示的是从区间 [ i , j ] [i,j] [i,j]不能合并过去。 [ i , j ] [i,j] [i,j]颜色都是 k k k的话这时候就是裸的区间 d p dp dp,如果 [ i , t ] , [ t + 1 , j ] [i,t],[t+1,j] [i,t],[t+1,j]这两个区间颜色不一样那么就可以分别做区间 d p dp dp

初始化显然如果是 f i , i , k f_{i,i,k} fi,i,k一定是 0 0 0,而 c n t i , j = j − i + 1 cnt_{i,j}=j-i+1 cnti,j=ji+1

我们再来处理最小堆数,我们做完合并以后就可以判断一下,如果 f i , j , k = i n f f_{i,j,k}=inf fi,j,k=inf的话说明 f i , t , k ∪ f t + 1 , j , k f_{i,t,k}\cup f_{t+1,j,k} fi,t,kft+1,j,k 颜色不一样并不能转移。

如果可以转移的话

f i , j , ( k + 1 ) m o d 3 = m i n ( f i , j , ( k + 1 ) m o d 3 , f ( i , t , k ) + f ( t + 1 , j , k ) + s j − s i ) f_{i,j,(k+1)mod3}=min(f_{i,j,(k+1)mod3},f(i,t,k)+f(t+1,j,k)+s_j-s_i) fi,j,(k+1)mod3=min(fi,j,(k+1)mod3,f(i,t,k)+f(t+1,j,k)+sjsi) c n t [ i ] [ j ] = 1 cnt[i][j]=1 cnt[i][j]=1, w i , j = m i n ( f i , j , 0 , f i , j , 1 , f i , j , 2 ) w_{i,j}=min(f_{i,j,0},f_{i,j,1},f_{i,j,2}) wi,j=min(fi,j,0,fi,j,1,fi,j,2)

因为 f i , j , k f_{i,j,k} fi,j,k最后得出的是相同颜色最小费用,如果有不能合并的话此时的 f i , j , k = + ∞ , c n t i , j = + ∞ , w i , j = + ∞ f_{i,j,k}=+\infty,cnt_{i,j}=+\infty,w_{i,j}=+\infty fi,j,k=+,cnti,j=+,wi,j=+不能保证是最终答案(因为最终答案是总费用最小)所以我们还需要对 c n t cnt cnt w w w再做一次区间 d p dp dp,优先把堆数少的先合并,如果堆数一样,那就按照费用最低来合并

#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
using namespace std;
const int N=500,inf=0x3f3f3f3f;
int n;
int f[N][N][5];
int a[N],w[N][N];
int cnt[N][N];
int s[N];
void solve()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	    cin>>a[i];
	for(int i=1;i<=n;i++)
	   s[i]=s[i-1]+a[i];
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j++)
		{
			for(int k=0;k<3;k++)
			f[i][j][k]=inf;
			cnt[i][j]=j-i+1;
		}
	}
	for(int i=1;i<=n;i++)
	{
		int x;
		cin>>x;
		f[i][i][x]=0;
	}
    for(int len=2;len<=n;len++)
    {
    	for(int i=1;i+len-1<=n;i++)
    	{
    		int j=i+len-1;
            for(int k=0;k<3;k++)
            {
            	int minv=inf;
            	for(int t=i;t<j;t++)
            	{
            		if(f[i][t][k]==inf||f[t+1][j][k]==inf)continue;
            		minv=min(minv,f[i][t][k]+f[t+1][j][k]);
            	}
            	if(minv==inf)continue;
            	cnt[i][j]=1;
            	f[i][j][(k+1)%3]=min(f[i][j][(k+1)%3],minv+s[j]-s[i-1]);
            	w[i][j]=min({f[i][j][0],f[i][j][1],f[i][j][2]});
            }
    	      
    	}
    }
    for(int len=2;len<=n;len++)
    {
    	for(int i=1;i+len-1<=n;i++)
    	{
              int j=i+len-1;
              for(int t=i;t<j;t++)
              {
              	if(cnt[i][j]>cnt[i][t]+cnt[t+1][j])
              	{
              		cnt[i][j]=cnt[i][t]+cnt[t+1][j];
              		w[i][j]=w[i][t]+w[t+1][j];
              	}
              	if(cnt[i][j]==cnt[i][t]+cnt[t+1][j])
              	{
              		w[i][j]=min(w[i][j],w[i][t]+w[t+1][j]);
              	}
              }
    	      
    	}
    }
    cout<<cnt[1][n]<<' '<<w[1][n]<<endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    int t=1;//cin>>t;
    while(t--)solve();
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值