第2题:我不和你玩
【题目描述】
在一个N*N矩阵的世界里生活着X,C,R三种元素。他们开始相处得很融洽。但后来有一天:
X:C们太不够意思了,最近他们经常作出不利于我们团结的事情。
R:对啊对啊,简直令人发指。
X:我们以后不跟他们玩了。
R:好!
于是X和R决定抵制C,要把所有的C赶到矩阵的左下角去。但是移动一个一个的元素是很费劲的。所以每次操作他们只能交换相邻的两个整行(包括这两行的X和R)。
你需要求出为了把所有的C移到左下角去(即所有的C的坐标x行y列均满足x小于等于y),所需要的最少操作步数。输入数据保证有解。
【输入格式】
第一行一个正整数T,代表有T组数据。
对于每组数据,第一行一个正整数N,代表矩阵的规模。然后接下来的N行每行有N个字符,必然是X,C,R中的一个。
【输出格式】
输出T行,每行一个整数,顺次为每组数据的最少操作步数。
【数据范围】
对于30分的数据,N小于等于8。
对于100分的数据,N小于等于40,T小于等于6。
【输入样例】
3
2
CR
CC
3
XRC
CXX
RCX
4
CCCX
CCXX
CCXX
CXXX
【输出样例】
0
2
4
这个是冒泡排序的模型。
首先可以发现,每一行都只和这一行最右边的那个C有关,
所以我们记录下每一行最右边的C位置。
观察发现,比较类似于冒泡排序(相邻交换)的思路,所以我们来验证。
从上到下找到不能满足条件的一行i,在不改变原来已经满足条件的行的情况下,我们选择最近(必定这样交换次数更小)的行j,且j移动到i之后,i移动到j之后能满足条件,且交换路径中的其它行,因为都是往下调整,所以只会更优不会更差。可以发现,这一步既满足了i,同时不改变原有的满足条件,因此必定是最小的步数使i满足条件。
同理的,每一个i我们都能找到局部最优的调整方法,将所有行都调整到满足条件的位置,就实现了全局最优。
所以冒泡排序是正确的,只是不同的,我们需要找到最近一个能够交换的,把它交换上来。
#include <cstdio>
#include <string>
#include <cstring>
long maxj[60];
char map[60][60];
long getint()
{
long rs=0;bool sgn=1;char tmp;
do tmp = getchar();
while (!isdigit(tmp)&&tmp-'-');
if (tmp == '-'){tmp=getchar();sgn=0;}
do rs=(rs<<3)+(rs<<1)+tmp-'0';
while (isdigit(tmp=getchar()));
return sgn?rs:-rs;
}
int main()
{
freopen("play.in","r",stdin);
freopen("play.out","w",stdout);
long T = getint();
while (T --)
{
long ans = 0;
long n = getint();
memset(maxj,0,sizeof maxj);
for (long i=1;i<n+1;i++)
{
for (long j=1;j<n+1;j++)
{
do map[i][j]=getchar();
while (map[i][j]!='C'&&map[i][j]!='R'&&map[i][j]!='X');
if (map[i][j] == 'C')
maxj[i] = j;
}
}
for (long i=1;i<n+1;i++)
{
if (maxj[i] > i)
{
long pos = 0;
for (long j=i+1;j<n+1;j++)
{
if (maxj[j] <= i)
{
pos = j;
break;
}
}
for (long j=pos-1;j>i-1;j--)
{
ans ++;
long tmp = maxj[j];
maxj[j] = maxj[j+1];
maxj[j+1] = tmp;
}
}
}
printf("%ld\n",ans);
}
return 0;
}