tyvj群上有人问就写了一下。。。似乎明白了很多东西
放心,O(常数)的算法肯定有,不要在意我前面的啰嗦。。。。
问题
G: 【基础】奖牌整理
时间限制: 1
Sec 内存限制: 32
MB
提交: 12 解决: 3
[提交][状态][讨论版]
题目描述
上完一天的课,小 Z 不但没有一丝疲惫感,反而觉得浑身是劲,好象又回到了童年, 他蹦蹦跳跳地回到家,吃过晚饭很快就做完了作业,抬头一看觉得房间太乱了,这不进入 初三后由于课业繁重一直没时间整理房间,现在有空了是时候该好好整理一下了。小 Z 首 先想要整理的是他那些心爱的奖牌,这些奖牌记录了小 Z 成长的足迹,其中有龙城少儿围 棋比赛的银牌,有华罗庚杯少年数学邀请赛的铜牌,份量最重的当数全国信息学奥林匹克 竞赛(National Olympiad in Informatics,简称 NOI)的金牌,小 Z 喜欢把他获得的奖牌 都挂在床头,并且成一字排开状,他每得到一枚奖牌就会在床头钉一颗钉子,将这枚奖牌 挂在所有奖牌的未尾,也就是说小 Z 的奖牌是按获得时间的先后顺序来排列的,现在小 Z 想把它们按金银铜的顺序排列,即所有的金牌挂在最前面,随后是银牌,最后是铜牌。小 Z 重排奖牌的方法很特别,他每次都是同时伸出双手各摘下一块奖牌,然后把这两块奖牌 的位置对换一下,即左手摘下的奖牌放到右手摘下的奖牌的位置,右手摘下的奖牌放到左手摘下的奖牌的位置,咦!这怎么看上去有点象交换赋值,太有才了!现在小 Z 想考考你, 给定奖牌序列,计算最少需要几次对换操作就可以将所有奖牌按金银铜牌的顺序排好。
输入
输入数据第一行只有一个正整数 N 表示奖牌序列的长度,其中 1≤N≤1000;第二行有 N 个大写字母,每个大写字母代表一枚奖牌,其中 G 代表金牌,S 代表银牌,B 代表铜牌。
输出
输出数据仅有一行包含一个整数,表示最少需要几次对换操作。
样例输入
9 SSGBBBSBG
样例输出
4
提示
样例解释
S S S<
G G
S< G
G G G
G<
S S S
S
B B< S
S S
B B
B B< S
B
B B B
B
S S< B
B B
B B
B B B
G
G G< S<
B
小 Z 的床头共有 9 枚奖牌,以上数据第一列为 9 枚奖牌的初始序列,每次操作将每列
中箭头指向的两个位置的奖牌对换,对换后变成右边一列的状态,经过 4 次对换操作所有
奖牌就按金银铜牌的顺序依次排好了,可以验证 4
步操作是必不可少的。
数据范围
10%的数据满足:奖牌种类只有两种
30%的数据满足:n≤10
60%的数据满足:n≤100
100%的数据满足:n≤1000
似乎蓝色的弄不掉了,就这样吧。。。
第一眼看到这题想到线段树。。我是不是和zhb神牛一样了。。。(自恋个屁!!)
然后我看清楚了初三得NOI金牌的更神牛小龙。。。跪。。我也初三为什么我没有NOI金牌
一开始我是想到这个做法其实跟冒泡排序很像,写了个程序:
ans=0;
for (i=1;i<=n-1;i++)
{
for (j=i+1;j<=n;j++)
{
if (a[i]<a[j]) {tmp=a[i];a[i]=a[j];a[j]=tmp;ans++;}
}
}
cout<<ans;
结果很惨:全WA。
为什么呢,其实冒泡排序不是最少步数。。原因是因为冒泡永远是从前往后死板地搜,不要证明了吧。。。
然后我又想了种算法,又爆了。。不好意思发上来。。
这里有一种很简单的解法。。
首先我们得搞清楚一个问题,最后金牌一定在最前面,银牌中间,铜牌最后,那么结果肯定是GGG……GGGSSS……SSSBBB……BBB这样的。。
也就是说我们得让金牌往前站,银牌站中间,剩下位置就是铜牌的。
那么,金牌的位置是从前往后数g个(共有g个金牌),银牌是g+1到第g+s个(共有s个银牌)
只要金牌位上不是金牌,或者银牌位上不是银牌,就一定需要一个操作使金牌或银牌归位,
那么只要统计出有多少金牌银牌需要归位,相加即为答案。。。
但是还有一种特例:金牌在银牌位上,银牌在金牌位上,那么答案应该-1
----------------------------------分析到此结束--------------------------------------------
#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
char a[1005];
int main()
{
int n,i,j,g,s,ans,t1,t2; //g表示金牌个数,s表示银牌个数,t1表示金牌位上银牌个数,t2表示银牌位上金牌个数
cin>>n;
g=0;s=0;
for (i=1;i<=n;i++)
{
cin>>a[i];
if (a[i]==‘G‘) g++;
if (a[i]==‘S‘) s++;
}
ans=0; t1=0; t2=0;
for (i=1;i<=g;i++)
{ if (a[i]!=‘G‘) {ans++;}
if (a[i]==‘S‘) {t1++;}
}
for (i=g+1;i<=g+s;i++)
{ if (a[i]!=‘S‘) {ans++;}
if (a[i]==‘G‘) {t2++;}
}
ans-=min(t1,t2);
cout<<ans;
return 0;
}
-------------------------------------------程序如上----------------------------------------------
这个OJ似乎和9018很像啊(^-^)
下附一段代码,是tyvj群一盏鲸鱼灯写的,给大家膜拜哈。。。
var
ans,n,i,j,k,tot1,tot2,tot3:longint;
a:array [0..1001] of longint;
ch:char;
begin
readln(n);
tot1:=0;
tot2:=0;
tot3:=0;
for i:=1 to n do
begin
read(ch);
case ch of
‘G‘:
begin
inc(tot1);
a[i] :=1;
end;
‘S‘:
begin
inc(tot2);
a[i] :=2;
end;
‘B‘:
begin
inc(tot3);
a[i] :=3;
end;
end;
end;
i:=1;
j:=tot1+i;
k:=tot2+j;
ans:=0;
while true do
begin
while (a[i] =1) and (i<=tot1) do
inc(i);
if i>tot1 then
break;
if a[i] =2 then
begin
while (a[j] <>1) and (j<=tot1+tot2) do
inc(j);
if j>tot1+tot2 then
begin
while (a[k] <>1) and (k<n) do
inc(k);
inc(ans);
a[i] :=1;
a[k] :=2;
end
else
begin
inc(ans);
a[i] :=1;
a[j] :=2;
end;
end
else
begin
while (a[k] <>1) and (k<=n) do
inc(k);
if k<=n then
begin
inc(ans);
a[i] :=1;
a[k] :=3;
end
else
begin
while (a[j] <>1) and (j<=tot1+tot2) do
inc(j);
inc(ans);
a[i] :=1;
a[j] :=3;
end
end;
end;
j:=tot1+1;
k:=tot2+j;
while true do
begin
while (a[j] =2) and (j<=tot1+tot2) do
inc(j);
if j>tot1+tot2 then
break;
while (a[k] <>2) and (k<=n) do
inc(k);
inc(ans);
a[j] :=2;
a[k] :=3;
end;
writeln(ans);
end.
我会告诉你我没看吗..但是他过了是真的...
应该很多人需要pas代码吧.......
jzxx奖牌整理(简单思考),布布扣,bubuko.com
jzxx奖牌整理(简单思考)
原文:http://www.cnblogs.com/seekdreamer/p/3782354.html