P8927 「GMOI R1-T4」Rain

文章介绍了如何解决一道编程竞赛题目,涉及在一条直线上建立法阵并计算行走最大距离的问题。作者尝试了贪心算法,通过排序和分类讨论p和q的关系,最终得出O(nlogn)复杂度的解决方案。代码实现包括对数组的排序和构建新的序列,以求出总距离和对应的法阵排列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

(大雾 一群神仙构造拆式子。我乱搞想骗分的贪心没有WA就离谱。顺便吐槽一下题意好难懂。我做题得跟用来判SPJ的checker.cpp一起看就离谱。另外祝大家1月1日新年快乐。


题目大意

需要在一条笔直的路上建 n n n 个法阵,编号为 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n

给定一个长度为 n n n 的数组 a a a,表示在 a 1 a_1 a1 a n a_n an 的位置建法阵,要给法阵编号。

需要来检测法阵效果,从 1 1 1 号法阵走到 2 2 2 号,从 2 2 2 号再走到 3 3 3 号,直到走到 n n n 号,再从 n n n 号走回 1 1 1 号。

由于法阵的特殊效果,从 i i i 个走到 i + 1 i+1 i+1 个的距离是 ∣ a i × p − a i + 1 × q ∣ \left|a_i\times p-a_{i+1}\times q\right| ai×pai+1×q。特别的,从 n n n 号走回到 1 1 1 号的距离是 ∣ a n × p − a 1 × q ∣ \left|a_n\times p-a_1\times q\right| an×pa1×q p , q p,q p,q 是给定的两个常数, a i , a i + 1 a_i,a_{i+1} ai,ai+1 是两个法阵的位置。

来求一下最大的行走距离,并输出对应法阵从 1 1 1 号到 n n n 号排列方法。
对于 100 % 100\% 100% 的数据满足 10 ≤ n ≤ 1 0 6 10\le n\le 10^6 10n106 1 ≤ p , q ≤ 1 0 5 1\le p,q \le 10^{5} 1p,q105 1 ≤ a i ≤ 1 0 5 1\le a_i\le 10^{5} 1ai105


思路

暴力深搜 O ( n n ) \mathcal{O}(n^n) O(nn),肯定T飞了。考虑优化。由于出题人连大样例的构造方式都不给了,小样例又特别水,所以可以猜测本题应该不会十分困难。

感觉本题好像可以贪心的样子,也没啥其他的思路了,想想试试。如果要贪心,那我就按照这样构造试试:

  • 找最大的放到新序列里
  • 找最小的放到新序列里
  • a a a 中删除这两个数

听着很绕,其实就是按照一大一小一大一小···这样排序。测一下小样例,发现不对。然后发现上述情况没有考虑 p , q p,q p,q,感觉这是不是错的原因。之后发现,如果 q q q 更大,那么按照小大小大···这么排序更好,因为这可以让大数变得更大,就算大的很小也不亏。所以分类讨论。当 p > q p>q p>q 时按照前者进行运算,否则按照后者进行。

时间复杂度 O ( n log ⁡ n ) O(n\log{n}) O(nlogn)

代码

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll MAXN=1e6+5;
ll n,p,q,a[MAXN],b[MAXN];
bool cmp(ll A,ll B){
    return A>B;
}
int main(){
    cin>>n;
    cin>>p>>q;
    for (int i = 1; i <=n ; ++i) {
        cin>>a[i];
    }
    if(p>q){
        sort(a+1,a+n+1);
    }else{
        sort(a+1,a+n+1,cmp);
    }
    ll l=1,r=n;
    ll sz=0;
    while (l<=r){
        b[++sz]=a[l];//放最小/大的
        b[++sz]=a[r];//放最大/小的
        l++;
        r--;
    }
    ll ans=0;
    for (int i = 1; i <n ; ++i) {
        ans+= abs(b[i]*p-b[i+1]*q);
    }
    ans+= abs(b[n]*p-b[1]*q);
    cout<<ans<<endl;
    for (int i = 1; i <=n ; ++i) {
        printf("%lld ",b[i]);
    }
    return 0;
}

总结

通过了分析题面和大胆猜测的出了贪心的做法。同时在经过错误后的分析得出了正解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值