题目大意
给定n行m列的座位,每个座位按编号从1~nm,一共有nm个观众观众编号1 ~ n*m,对于第i个观众有一个视力a[i],对于不同视力的两个观众i和j,如果a[i]<a[j],如果他们分到的座位使si和sj,那么必须有si<sj(si和sj为座位编号)。给每个人分配好座位之后,每个观众按照编号先后入场,如果一个人走到自己座位时,有k个人给他让路让他进去,那么针对这个人的不方便值为k。比如一行5列的座位,按照如下方式分给观众:1 2 3 4 5,也就是第i个位子坐观众i,因为按观众顺序就坐,那么2号观众就坐的时候需要1号观众让路,所以2号观众的不方便值为1,3号观众就坐的时候需要1和2号观众让路,所以不方便值为2,所以这个案例的不方便值和为0+1+2+3+4=10。要求求出最小的不方便值满足题目条件。
解题思路
首先因为对于a[i]<a[j]时,需要s[i]<s[j]。也就是对于任意两个人视力小的人需要坐在编号更小的位子,所以按照视力排序是显然的,如果n=1也就是座位只有1行的话,对于视力相同的人来说,一定是编号更大的更早入座比较好,比如3 2 1 肯定比1 2 3 ,2 1 3 要好,所以想到了一种策略就是视力相同的人编号大的放在前面。这种策略对于只有一行是肯定成立的,但是对于多行的情况不成立。看如下示例:
假设有10个人,2行2列的座位,a[1]=1,a[2]=1,a[3]=2,a[4]=1,按照这个策略排序后顺序为4 2 1 3也就是如下入座方式
4 2
1 3
不方便值和为1
显然有更好的方式
2 1
4 3
不方便值和为0
问题出在我们对所有人按照视力从小到大排序后,对于视力相同的人所有的都按照编号从大到小排序了,对于视力相同的,行数前面的编号应该要小,行数后面的编号应该要大。
最终的策略就有了,先对所有的按照视力从小到达排序,视力相同的人编号大的排在后面。然后二次排序,每m个人,也就是对每行排序,按照视力从大到小排序,视力相同的人编号大的排在前面。
代码示例
#include<bits/stdc++.h>
using namespace std;
struct node
{
int a;//视力
int p;//编号
}que[305*305];//观众
int T;
bool cmp2(const node& x,const node& y)//按照视力从小到大排序,视力相同按照编号从小到大排序
{
if(x.a!=y.a)
return x.a<y.a;
return x.p<y.p;a
}
bool cmp(const node& x,const node& y)//按照视力从小到大排序,视力相同按照编号从大到小排序
{
if(x.a!=y.a)
return x.a<y.a;
return x.p>y.p;
}
int main()
{
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);//n行m列
for(int i=1;i<=n*m;i++)//输入每个观众的视力
{
scanf("%d",&que[i].a);
que[i].p=i;
}
int ans=0;
sort(que+1,que+1+n*m,cmp2);//先对所有人按照视力从小到大排序,视力相同的人编号小的靠前
for(int i=1;i<=n;i++)//对于每一行视力相同的人编号大的在前面
{
sort(que+m*(i-1)+1,que+m*(i-1)+1+m,cmp);
}
for(int i=1;i<=n;i++)//对每个人求他的不方便值
{
for(int j=m*(i-1)+2;j<=i*m;j++)
{
for(int k=m*(i-1)+1;k<j;k++)
{
if(que[k].p<que[j].p)
ans++;
}
}
}
printf("%d\n",ans);
}
return 0;
}