牛客周赛 Round 105【D题:重排构造题解】

📖 题目描述

给定一个长度为 n 的数组,数组中只包含整数 12。要求重新排列数组,使得相邻元素的最大公因数之和等于给定值 k

输入格式:

  • 📥 第一行:两个整数 n, k

  • 📥 第二行:n 个整数,每个都是 12

输出格式:

  • ❌ 如果无解,输出 -1

  • ✅ 否则输出任意一个满足条件的重排数组

💡 解题思路

相邻两个数的最大公因数:

  • gcd(1,1) = 1

  • gcd(1,2) = 1

  • gcd(2,1) = 1

  • gcd(2,2) = 2

👉 只有两个 2 相邻时贡献为 2,其他情况都贡献 1

📐 数学构造

设数组中有 x 对相邻的 2,则:

  • x 对的贡献:x × 2 = 2x

  • 剩余 (n-1-x) 对的贡献:(n-1-x) × 1

  • 总和 = 2x + (n-1-x) = n-1+x

要求:

\(n−1+x=k⇒x=k−(n−1)\)

🛠️ 算法设计

根据 x 的值分两种情况处理:

🎯 情况1:x = 0(不需要相邻的2)

  • 条件检查:所有 2 都不能相邻 → b ≤ a+1

  • 构造策略:交替排列 1 2 1 2 1 2...或者2 1 2 1 2 1...

if (x==0) {
    if (b>a+1) {
      cout<<"-1"<<endl;
      return 0;
    }
  //如果2的数量更多,构造2 1 2 1 2 1...
    if (b>=a) {
      for (int i=0;i<a;i++)
        cout<<"2 1 ";
      for (int i=0;i<b-a;i++)
        cout<<"2 ";
    }
  //1的数量更多,构造1 2 1 2 1 2 ...
    else {
      for (int i=0;i<b;i++)
        cout<<"1 2 ";
      for (int i=0;i<a-b;i++)
        cout<<"1 ";
    }
  }

🎯 情况2:x > 0(需要恰好 x 对相邻的2)

  • 条件检查

    1. 需要至少 x+12 来形成 x 对相邻的 2

    2. 需要足够的 1 来分隔剩余的 2

  • 构造策略

    1. 先放置 x+1 个连续的 2

    2. 1 分隔剩余的 21 2 1 2...

    3. 最后放置剩余的 1

else {
    int bb=b-(x+1);
    if (a<bb) {
      cout<<"-1"<<endl;
      return 0;
    }
  //先放置 x+1 个连续的 2
    for (int i=0;i<x+1;i++)
      cout<<"2 ";
  //用 1 分隔剩余的 2
    for (int i=0;i<bb;i++)
      cout<<"1 2 ";
  //放置剩余的 1
    for (int i=0;i<a-bb;i++)
      cout<<"1 ";
  }

🖥 C++ 实现

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long


int main() {
  int n,k;
  cin>>n>>k;
  int a=0;
  int b=0;
  for(int i=1;i<=n;i++) {
    int x;
    cin>>x;
    if (x==1)
      a++;
    else if (x==2)
      b++;
  }
  int x=k-(n-1);

  if (x<0||x>=b) {
    cout<<"-1"<<endl;
    return 0;
  }

  if (x==0) {
    if (b>a+1) {
      cout<<"-1"<<endl;
      return 0;
    }
    if (b>=a) {
      for (int i=0;i<a;i++)
        cout<<"2 1 ";
      for (int i=0;i<b-a;i++)
        cout<<"2 ";
    }
    else {
      for (int i=0;i<b;i++)
        cout<<"1 2 ";
      for (int i=0;i<a-b;i++)
        cout<<"1 ";
    }
  }
  else {
    int bb=b-(x+1);
    if (a<bb) {
      cout<<"-1"<<endl;
      return 0;
    }
    for (int i=0;i<x+1;i++)
      cout<<"2 ";
    for (int i=0;i<bb;i++)
      cout<<"1 2 ";
    for (int i=0;i<a-bb;i++)
      cout<<"1 ";
  }
  cout<<endl;
  return 0;
}

📊 复杂度分析

  • 时间复杂度O(n)(输入输出)

  • 💾 空间复杂度O(1)(常数额外空间)

🎯 总结

题目的样例有点不太全,我的第一次代码

输入n=7, k=6, [1 1 1 2 2 2 2]

输出1 2 1 2 1 2 2

但是这个的gcd和值为7;

正确的输入应该是: 2 1 2 1 2 1 2

明显错误,但是ac了。

后面我就在x = 0的判断里,又加了两个判断,判断是应该先输出 1 还是 2。

posted @ 2025-08-18 17:24  开珥  阅读(12)  评论(0)    收藏  举报