Codeforces Global Round 16 D2. Seating Arrangements (hard version)

题目大意

给定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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值