Today来分享一下完美的代价
我在做的时候发现了一位up主写的很好,我加一点自己的理解帮助萌新能够完全理解,简直就是保姆级别的教学
首先是题目
解析:本题妙用了————贪心(如果不了解可以直接看下面的举例)
如图:
由此开始,先定义i,k,两个变量,i 从字符串最左边开始往右遍历,k则从最右边开始往左遍历
i 先不改变,让k自减往左遍历(k - -),如果a[ i ] 和 a[ k ]不一样,则k继续往左遍历,直到和a[ i ]一样,如下
定义一个变量 j 等于字符长度(可以理解为一直指最后一个未经过交换对称的字符),则下标最大可到 j-1
则将a[ k ]移动到于a[ i ]对称的位置,可以通过a[ k ] = a[ k+ 1]进行交换,条件为k < j , 最后 a[ j ] = a[ i ]即可,并且在此过程,可以定义一个记录交换次数的变量sum=0,每次交换都进行自增(sum++),由于交换放置后的字符已经形成对称,所以无需改变其位置,下一次下一组字符的交换无需到达该位,可以让 j = j - 1,如下 ,j 原指a,a已经是交换对称了,可以忽略,j指向前一个,i++,接下来只需考虑红框内的字符数组,交换好的无需再管。
每一轮都让k==j,这保证了k每次都从未经对称的末尾字符开始往左遍历,接下来k遇到m,然后交换,a[ k ] = a[ k+1] ,条件为k < j,最后a[ j ] == a[ i ]即可,和上一步一样的方法,以此类推。
但是,如果一直找不到相等的字符,怎么办?
像这种情况,如果一直找不到相同的,最后会遇到 i==k 的情况,当i == k的时候,我们可以知道 a[ i ]在数组内独一无二,我们是不是可以把它移动到中间,共移动(数组长度/2 - i)次,这个时候可以不需要真的进行交换,并无影响结果(因为独一无二数放在中间后,由于是经过对称移动的,所以并不能动其位置,所以可以忽略,就和没移动一个样,交换的时候也要跳过,因此不必进行这一交换步骤)。
如果出现两次 i = = j的情况,则代表数组内存在两个独一无二的字符,不可能存在完美列,可以结束程序,打印Impossible,
当条件 i < j 不满足的时候,代表对称交换已经执行完毕,可以输出sum总值。
代码如下:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i,j,k,sum=0,N,t;
char f[8001];
scanf("%d",&N);
scanf("%s",f);
t=N-1;
bool flag=false;
for(i=0;i<t;i++)
{
for(j=t;j>=i;--j)
{
if(i==j)
{
if(N%2==0||flag)//如果N为偶数但是f[i]又是唯一的字母 flag判断有两次与f[i]相同的字符
{
printf("Impossible");
return 0;
}
else
sum+=N/2-i;//f[i]移动至中间所需要的次数
flag=true;//只有自身相等
break;
}
if(f[i]==f[j])
{
for(k=j;k<t;k++)
{
f[k]=f[k+1];
sum++;
}
f[k]=f[i];
--t;
break;
}
}
}
printf("%d",sum);
return 0;
}
这是那位博主文章的链接,有需要可以看看(19条消息) 蓝桥杯基础训练1573:完美的代价(C语言实现)_厌笑的小鸡蛋-CSDN博客_c语言完美的代价代码
还有不懂的可以评论区问我哟