洛谷 P4829 kry loves 2048——題解

Nightmares_oi發表於2024-09-07

洛谷P4829題解


傳送錨點


摸魚環節

kry loves 2048

題目背景

kls是一個人贏。

題目描述

kls最近在玩一款類似2048的遊戲,規則是這樣的:

一開始,有\(n\)個方塊,每個方塊上有一個\(1\)\(m\)的整數。

kls可以進行兩種操作:

  1. 選擇兩個數字相同的方塊(不一定要相鄰),將它們合併成一個數字為原來的兩倍的方塊;

  2. 減小一個方塊上的數字。

操作的次數沒有限制,最終的得分為所有方塊上的最大的數字。

因為kls要去陪妹子了,沒有時間繼續玩,他想讓你幫忙計算一下,最多能得到多少分。

輸入格式

因為資料可能很大,讀入容易超時,所以kls給你們提供了一個c++的隨機數生成器。

void generate_array(int a[], int n, int m, int seed) {
    unsigned x = seed;
    for (int i = 0; i < n; ++i) {
        x ^= x << 13;
        x ^= x >> 17;
        x ^= x << 5;
        a[i] = x % m + 1;
    }
}

把這個函式複製到你的程式裡。用一個足夠大的陣列,以及輸入資料給出的\(n\)\(m\)\(seed\)作為引數,呼叫這個函式。然後這個陣列裡就是一開始的方塊上的數字(下標從0開始)。

輸入一行三個數\(n,m,seed\),含義如上。

輸出格式

一行一個數,表示最大得分。

樣例 #1

樣例輸入 #1

5 10 233

樣例輸出 #1

24

樣例 #2

樣例輸入 #2

5 50 3

樣例輸出 #2

48

樣例 #3

樣例輸入 #3

1000 1000 666

樣例輸出 #3

374784

提示

樣例解釋

樣例1生成出來的數是 6 10 7 5 4。

樣例2生成出來的數是 8 12 48 4 4。

資料範圍

對於30%的資料,\(n, m \le 10\)

對於60%的資料,\(n, m \le 10^5\)

對於100%的資料,\(n, m \le 10^7\)\(1 \le seed \le 10^9\)


眾所周知,oier們喜歡處理一些奇奇怪怪非常有意思的問題。同時,oier們都樂於助人,每天都在幫不同的人處理不同的問題(我cow,今天這個人怎麼還有妹子可以陪),面對kls的困難之處,咱們oier酸了當然得幫他解決。所以,正解是幫他陪妹子。


正片開始

1.草率程式碼

類似於合併果子,每次應該選取小於數\(a\)
中的最大值進行合併,考慮用優先佇列維護。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e7+5;
ll n,m,seed,ans;
ll a[N];
priority_queue<ll,vector<ll>,greater<ll> > q;//小數在前
void cao(ll n, ll m, ll seed) //n個數,m域 
{
    unsigned x=seed;
    for(ll i=0;i<n;++i) 
	{
        x^=x<<13;
        x^=x>>17;
        x^=x<<5;
        a[i]=x%m+1;
    }
}
int main()
{
    cin>>n>>m>>seed;
    cao(n,m,seed);
    for(int i=0;i<=n;i++) q.push(a[i]);
    while(q.size()>1)
    {
    	ll x=q.top(),y,z;
    	q.pop();
    	y=q.top();
    	q.pop();
    	z=2*x;
    	ans=max(ans,max(x,y));//更新當前答案
    	q.push(max(z,y));//比較是選擇較小數*2更優,還是較大數更優。
	}
	ans=max(ans,q.top());
	cout<<ans;
    return 0;
}

然後突然發現,程式碼TLE,還T了4個點,看著黃色的60,內心萬分不甘,於是開啟題解區認真分析下複雜度。發現程式碼的複雜度為\(nlogn\)\(n=1e7\)顯然是祭得很慘。

2.最佳化環節

很明顯這個\(logn\)肯定是得去掉的,由於在排序上的複雜度不夠優,所以選擇用計數排序預處理解決問題,這樣複雜度就降到了\(o(n)\),於是快樂AC了。

code:

for(int i=mina;i<=maxa;i++)
    for(int j=1;j<=t[i];j++)
        a[++len]=i;

完整程式碼

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e7+5;
int n,m,seed,b[N],a[N],t[N];
ll maxa=0,mina=0x3f;
ll len=0,cnt=1;
queue<ll>q;
void cao(int n, int m, int seed) 
{
    unsigned x=seed;
    for(int i=1;i<=n;++i) 
	{
        x^=x<<13;
        x^=x>>17;
        x^=x<<5;
        b[i]=x%m+1;
    }
}
ll get()
{
    if(q.empty()) return a[cnt++];
    ll x=q.front();
    if(cnt==n+1||a[cnt]>x)
    {
        q.pop();
        return x;
    }
    return a[cnt++];
    
}
int main()
{
    cin>>n>>m>>seed;
    cao(n,m,seed);
    for(int i=1;i<=n;i++)
    {
        t[b[i]]++;
        maxa=max(maxa,1ll*b[i]);
        mina=min(mina,1ll*b[i]);
    }
    for(int i=mina;i<=maxa;i++)
        for(int j=1;j<=t[i];j++)
            a[++len]=i;//計數排序
    for(int i=1;i<n;i++)
    {
        ll x=get(),y=get();
        q.push(max(x*2,y));
    }
    cout<<q.front()<<endl;
    return 0;
}

完結收工!!!!!

個人主頁

看完點贊,養成習慣

\(\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\)

相關文章