Girls Love 233
Accepts: 30
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.