• Bestcoder#92&HDU 6017 T3 Girl loves 233 DP


    Girls Love 233

    Accepts: 30    Submissions: 218   Time Limit: 2000/1000 MS (Java/Others)
    Memory Limit: 65536/65536 K (Java/Others)
    问题描述
    除了翘课以外,结识新的妹子也是呃喵重要的日程安排之一。
    这不,呃喵又混进了一个叫做ACgirls的女生群里,来达成自己不可描述的目的。
    然而,呃喵只会喵了个咪地说话,于是很容易引起注意。为了掩饰自己的真实身份,呃喵每次说话都小心翼翼。
    她知道,很多女生都喜欢说"233",然而呃喵想说的话一开始就确定好了,所以她要对这句话做修改。
    这句话的长度为n,语句里的字符不是'2'就是'3'。
    呃喵的智力非常有限,只有m点。她每次操作可以交换两个相邻的字符,然而代价是智力-2。
    现在问你,在使得自己智力不降为负数的条件下,呃喵最多能使这个字符串中有多少个子串"233"呢?
    如"2333"中有一个"233","232323"中没有"233"
    输入描述
    第一行为一个整数T,代表数据组数。
    接下来,对于每组数据——
    第一行两个整数n和m,分别表示呃喵说的字符串长度 以及呃喵的智力
    第二行一个字符串s,代表呃喵具体所说的话。
    
    数据保证——
    1 <= T <= 1000
    对于99%的数据,1 <= n <= 10, 0 <= m <= 20
    对于100%的数据,1 <= n <= 100, 0 <= m <= 100
    输出描述
    对于每组数据,输出一行。
    该行有1个整数,表示最多可使得该字符串中有多少个"233"
    输入样例
    3
    6 2
    233323
    6 1
    233323
    7 4
    2223333
    输出样例
    2
    1
    2

    思路

    方法一:

      (1)交换2和2或交换3和3 是一种浪费

      所以我们得到一个贪心原则

    结果中的2的先后顺序一定和原字符串中的2的先后顺序是一样的

    什么,听说你想举反例?

    假设第i个2和第i+1个2 的先后顺序交换了

    那么在交换的过程中必定会有‘22’之间的交换,那么这样不换了既不会改变顺序,也是同样的结果

    所以贪心原则是成立的!

    (2)用p[i]表示第i个2在p[i],假设全是3,然后我们一个一个确定2的位置

    判定原则

     如果前一个2后当前2位置相差3那么就有一个233

    (3)基于(1)的贪心原则划分阶段,用f[i,j,p]表示处理完第i个2,花j次交换,第i个2在p时最多的233个数

    lj表示上次j的位置,lp表示上一次2的位置,那么:

    后面的条件保证现在的位置在lp之后3个以及以上(保证贪心原则和判定原则)

    可以用递归实现

     

     1 program gl233;
     2 uses math;
     3 const
     4   inf='233.in';
     5   outf='233.out';
     6 var
     7   n,m,ii,t,tot:longint;
     8   tmp:char;
     9   p:array[1..100] of longint;
    10   check,f:array[0..100,0..100,0..50] of longint;
    11 
    12 procedure init;
    13 var
    14   i:longint;
    15 begin
    16   tot:=0;
    17   readln(n,m);
    18   for i:= 1 to n do
    19   begin
    20     read(tmp);
    21     if tmp='2' then
    22     begin
    23       inc(tot);
    24       p[tot]:=i;
    25     end;
    26   end;
    27 end;
    28 
    29 function pd233(app,i:longint):longint;
    30 begin
    31     if i<=1 then exit(0);
    32     if app>=3 then exit(1)
    33       else exit(0);
    34 end;
    35 
    36 function find(i,lp,iq:longint):longint; //lp=last_p;   iq ?
    37 var
    38   s,e,t1:longint;
    39 begin
    40 
    41   if i>tot then if n-lp>=2 then exit(1) else exit(0);
    42    if check[i,lp,iq]<>i then check[i,lp,iq]:=ii
    43      else exit(f[i,lp,iq]);
    44 
    45   s:=max(p[i]-iq,lp+1);
    46   e:=min(p[i]+iq,n);
    47    for t1:=s to e do
    48      f[i,lp,iq]:=max(f[i,lp,iq],find(i+1,t1,iq-abs(t1-p[i]))+pd233(t1-lp,i));
    49    exit(f[i,lp,iq]);
    50 
    51 end;
    52 
    53 begin
    54   //assign(input,inf);
    55   //assign(output,outf);
    56   reset(input);
    57   rewrite(output);
    58 
    59   readln(t);
    60   for ii:= 1 to t do
    61    begin
    62      init;
    63      m:=m div 2;
    64      writeln(find(1,0,m));
    65    end;
    66 
    67   close(input);
    68   close(output);
    69 end.

    But! pascal会TLE 于是我们要用claris的方法!


    还是上面那贪心原则
    然后官方题解已经讲的很清楚了

    最后还要提下,claris老师提供了一个复杂度更加优秀的O(n * n * n * 3)的做法,大体如下——

    考虑最后形成的串是'2'与'3'归并排序后的结果。

    于是我们显然需要知道——

    1.当前选了多少个2

    2.当前选了多少个3

    3.当前用了多少次交换

    4.影响决策的状态,这里有3种情况——

    a.不存在前一个'2',或者前一个'2'后面已经有了足够的'3',记做状态0
    
    b.前一个'2'后面只有0个'3',记做状态1
    
    c.前一个'2'后面只有1个'3',记做状态2
    

    用g2与g3表示2与3个个数,用p[]记录所有2的位置,于是就可以有——

    for(i)for(j)for(k)for(t)if(~f[i][j][k][t])
    
    {
        if (i < g2)//我们可以考虑接下来放置一个'2'
        {
            int sum = k + abs(i + j + 1 - p[i + 1]);
            if (sum <= m)gmax(f[i + 1][j][sum][1], f[i][j][k][t]);
        }
        if (j < g3)//我们可以考虑接下来放置一个'3'
        {
            int sta = t; int cnt = f[i][j][k][t];
            if (sta)
            {
                if (sta < 2)++sta;
                else sta = 0, ++cnt;
            }
            gmax(f[i][j + 1][k][sta], cnt);
        }
    }最后在f[g2][g3][0~m][0~2]中更新答案。
    
     1 program gl233;
     2 uses math;
     3 const
     4   inf='233.in';
     5   outf='233.out';
     6 var
     7   n,m,ii,t,tot,num3,num2,sta,cnt,next:longint;
     8   tmp:char;
     9   p:array[1..100] of longint;
    10   check,f:array[0..100,0..100,0..50,0..2] of int64;
    11 
    12 procedure init;
    13 var
    14   i:longint;
    15 begin
    16   tot:=0;
    17   readln(n,m);
    18   for i:= 1 to n do
    19   begin
    20     read(tmp);
    21     if tmp='2' then
    22     begin
    23       inc(tot);
    24       p[tot]:=i;
    25     end;
    26   end;
    27   num2:=tot;
    28   num3:=n-tot;
    29 end;
    30 
    31 procedure find; 
    32 var
    33   i,j,k,t,ans:longint;
    34 begin
    35   for i:=0 to num2 do
    36     for j:= 0 to num3 do
    37       for k:= 0 to m do
    38         for t:= 0 to 2 do
    39          f[i,j,k,t]:=-1;    //初始化为-1便于后面max
    40 
    41   f[0,0,0,0]:=0;            //边界
    42   for i:=0 to num2 do  
    43     for j:= 0 to num3 do
    44       for k:= 0 to m do
    45         for t:= 0 to 2 do
    46          if f[i,j,k,t]<>-1 then  //f[i,j,k,t]已经是一个更新过的状态
    47           begin
    48             if i<num2 then       //有足够的2,下一个放2
    49             begin
    50                 next:=k+abs(i+j+1-p[i+1]);   //i+j+1是当前点
    51                 if next<=m then f[i+1,j,next,1]:=max(f[i+1,j,next,1],f[i,j,k,t]);  //更新i+1个点
    52             end;
    53             if j<num3 then       //有足够的3,下一个放3
    54             begin
    55                 cnt:=f[i,j,k,t]; 
    56                 sta:=0;
    57                 //t=0前面已经有足够3,那不用加233的个数
    58                 if t=1 then sta:=2;      //前面有0个3又放了一个3,那么就有1个233啦
    59                 if t=2 then inc(cnt);    //前面有1个3又放了一个3 ,那么就多了一个233
    60                 //sta<>1因为放了3 那就不会有0个3 了
    61 
    62                 f[i,j+1,k,sta]:=max(f[i,j+1,k,sta],cnt);   //更新
    63             end;
    64           end;
    65 
    66     ans:=0;
    67       for k:= 0 to m do
    68         for t:= 0 to 2 do
    69           ans:=max(ans,f[num2,num3,k,t]);  //findmax!
    70     writeln(ans);
    71 end;
    72 
    73 begin
    74   //assign(input,inf);
    75  // assign(output,outf);
    76   reset(input);
    77   rewrite(output);
    78 
    79   readln(t);
    80   for ii:= 1 to t do
    81    begin
    82      init;
    83      m:=m div 2;
    84      find;
    85    end;
    86 
    87   close(input);
    88   close(output);
    89 end.
  • 相关阅读:
    分布式 and 集群
    时间复杂度



    线性表 & 散列表
    栈 & 队列
    数组 & 链表
    数据结构&算法
    Docket 容器引擎
  • 原文地址:https://www.cnblogs.com/bobble/p/6591591.html
Copyright © 2020-2023  润新知