【NOIP20018复习】B(贪心/DP)

B

时间限制:10000MS内存限制:256000KB

题目描述

有 n 个瓶子,它们在桌子上排成一排。第 i 个瓶子的颜色为 ci,每个瓶子都有灵性,每次操作可以选择两个相邻的瓶子,消耗他们颜色的数值乘积的代价将其中一个瓶子的颜色变成另一个瓶子的颜色。
         现在要让所以瓶子的颜色都一样,操作次数不限,但要使得操作的总代价最小。
 

输入

输入文件为 colour.in。
一个测试点内多组数据。
第一行,一个正整数 T,表示数据组数。
每组数据内:
第一行一个整数 n,为瓶子的个数。
第二行共 n 个整数,第 i 个整数为第 i 个瓶子的颜色 ci。

输出

输入文件为 colour.out。
共一行,一个整数,为最小的总代价。

输入样例复制

4
7 4 6 10

输出样例复制

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 共10个测试点。 对于1,2测试点,n<=3,ci<=10^5; 对于3,4测试点,n<=10,ci<=4; 对于5测试点,n<=300,ci<=4; 对于6测试点,n<=300,ci<=5; 对于7,8,9,10测试点,n<=300,ci<=10^5; 对于所有数据,满足T<=10,n<=300,ci<=10^5,且保证前8个测试点随机。

题解: 贪心。假设将所有瓶子变为c[i],将所有c[i]瓶子标记,则任意两个c[i]瓶子间互不影响  
                     将一个区间变为c[i]有两种策略,一种是将区间直接变为c[i]
                                                                       第二种是先将区间变为区间最小值,再全部变为c[i](显然只改变一次最优)
                     将每个区间代价最小相加即为答案
           注意:默认a[0]与a[n+1]为当前颜色使代码细节减少

          

 

const

  maxn=400;

var

  a,sum:array[0..maxn+1]of int64;

  c:array[1..100000]of boolean;

  v:array[0..maxn]of boolean;

  b:array[1..maxn,1..maxn]of int64;

  ans,sum1,sum2,ssum:int64;

  n,i,j,k,len,head,tail,pp,hh,t,hhead,ttail:longint;

function min(a,b:int64):int64;

begin

  if a<b then exit(a) else exit(b);

end;

procedure init;

var

  i,j:longint;

begin

  readln(n);

  for i:=1 to n do

  begin

    read(a[i]);

    sum[i]:=sum[i-1]+a[i];

  end;

  for i:=1 to n do

  begin

    b[i,i]:=a[i];

    for j:=i+1 to n do

      b[i,j]:=min(b[i,j-1],a[j]);

  end;

  ans:=1000000000000;

  fillchar(c,sizeof(c),false);

end;

begin

  readln(t);

  for hh:=1 to t do

  begin

  init;

  for i:=1 to n do

  begin

    if not c[a[i]] then

    begin

      ssum:=0;a[0]:=a[i];a[n+1]:=a[i];

      fillchar(v,sizeof(v),false);

      c[a[i]]:=true;

      v[i]:=true;

      for j:=0 to n+1 do

        if a[j]=a[i] then v[j]:=true;

      hhead:=0;

      for j:=1 to n+1 do

        if (v[j]) then

        begin

          ttail:=j;

          head:=hhead+1;tail:=ttail-1;

          if head>tail then

          begin

            hhead:=j;

            continue;

          end;

          sum1:=0;sum2:=0;len:=0;

        //  pp:=head;

         // if head=1 then pp:=j;

        //  if tail=j then pp:=head;

          for k:=head to tail do

          begin

            if a[k]<>a[i] then sum1:=sum1+a[k];

            if a[k]<>b[head,tail] then

            begin

              sum2:=sum2+a[k];

              //inc(len);

            end;

          end;

          sum1:=sum1*a[i];

          sum2:=sum2*b[head,tail]+b[head,tail]*(tail-head+1)*a[i];

          ssum:=ssum+min(sum1,sum2);

          hhead:=j;

        end;

    end;

    ans:=min(ans,ssum);

  end;

  writeln(ans);

  end;

end.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值