1. 程式人生 > 其它 >G-Game of Swapping Numbers

G-Game of Swapping Numbers

Game of Swapping Numbers

題意

  給定兩個長度為\(n\)的陣列\(a、b\),計算\(\sum_{i=1}^n\mid a_i-b_i\mid\)。現要對\(a\)陣列中任意兩個元素交換位置,經過\(k\)次操作,輸出能夠獲取到的最大值。

思路

  假設給定陣列\(a=\left\{2,8,9\right\}\),\(b=\left\{7,6,10\right\}\),在數軸上畫出
  如果想要使得獲取到的值變大,我們就要想什麼樣的區間是對我們有益的。從上圖我們可以看出,對於\((6,8),(9,10)\)兩個沒有交集的區間,無論我們如何交換兩個區間的端點,區間的間隔都將會加入結果中,也就是我們可以獲取更大的值。而對於區間\((2,7),(6,8)\)

而言,一旦交換\(6,7\)就會導致我們的區間長度變小,因此從獲取最大值的角度上,我們交換選擇的端點,其貢獻為\(2*(min(a_i,b_i)-max(a_j,b_j))\),如果小於\(0\),說明存在交集,不需要交換。
  由於題目存在交換次數的限制,所以我們還需要考慮次數的影響。由於當\(n>2\)時,根據抽屜原理,\(a_i>b_i\)或者\(a_i<b_i\)中的一種情況必然出現兩次,因此對於至多\(k\)次操作是等價於必須\(k\)次操作的,因為我們只需要不斷交換兩個同一情況的區間,直到交換次數達到\(k\)次。而當\(n==2\)時,我們只需要對\(k\)的奇偶性進行判斷是否需要交換即可。
  因此,我們只需要用兩個資料分別儲存\(min(a_i,b_i),max(a_i,b_i)\)
,再分別按照從大到小、從小到大的順序排好,依次取前\(min(n,k)\)個數計算貢獻即可(這樣每次貢獻的區間長度是最大的)。

參考程式碼

點此展開
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<int,int> PII;

const int N=5e5+10;

ll n,k;
ll a[N],b[N];
ll u[N],d[N];//u[i]儲存較小值,d[i]儲存較大值

bool cmp(const ll &l,const ll &r)
{
    return l>r;
}

int main()
{
    cin>>n>>k;

    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];

    ll res=0;//注意結果可能很大
    for(int i=1;i<=n;i++)
    {
        res+=abs(a[i]-b[i]);
        u[i]=min(a[i],b[i]);
        d[i]=max(a[i],b[i]);
    }

    if(n==2)
    {
        if(k&1)
            swap(a[1],a[2]);
        res=abs(a[1]-b[1])+abs(a[2]-b[2]);
        cout<<res<<endl;
        return 0;
    }

    sort(u+1,u+n+1,cmp);
    sort(d+1,d+n+1);

    for(int i=1;i<=min(n,k);i++)
    {
        if(u[i]>d[i])
            res+=2*(u[i]-d[i]);
         else
            break;
    }

    cout<<res<<endl;

    return 0;
}