P4829题解
(在洛谷上弄了半天不知道该咋弄,最终还是决定转战博客)
首先看到这个题,作为一名2048资深玩家,肯定是一直合并最小的,直到只剩下一个数,那个就是最大值。
但这个题多给了一种操作,就是使某个方块数值降低。
那么,依然是找最小的两个合并。
但是注意
当最小的两个数为a,b,且2a<b,那么我可以完全不管a,因为合并后的值比合并前的值要小
好了,目前大体思路已经确定,那么剩下就是算法的问题
如何找出两个最小的值?
我的第一想法就是优先队列,毕竟O(nlog(n))的效率不算太低;
于是:
优先队列
`
priority_queue<int ,vector<int> ,greater<int> > q;
scanf("%d%d%d",&n,&m,&seed);
generate_array(a,n,m,seed);
while(q.size()-1)
{
int i=q.top();
q.pop();
int j=q.top();
q.pop();
if(max(i,j)>2*min(i,j)) q.push(max(i,j));
else q.push(2*(min(i,j)));
}
printf("%d",q.top());
return 0;
`(初始化时便把数据一个一个放入q中)
然后……

痛定思痛,既然优先队列都会TLE,那么我们必须找到一种O(n)的排序方法
那么结果呼之欲出——桶排;
当然,我们不可能一直拿桶来维护,因为可能会有一些值极大。
因此:
桶排+队列
首先在初始化时进行桶排,把他们放入队列q中,当初始化结束时,q中必定是升序排列
而每次处理后,我们将得到的结果放入另一个队列p中,那么p中也必定是升序排列;
因此,每次我只需要比较两个队列头即可
尤其要注意的是,此题数据可能会很大,因此至少都得开long long
代码实现:
`
using namespace std;
const int maxn=10000010;
int a,n,m,seed,ma,t[maxn];
queue<ll> q,p;
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 = x % m + 1;
t[a]++;
if(a>ma)ma=a;
}
}
ll qmin(queue<ll> &i,queue<ll> &j)
{
if(i.empty())
{
ll k=j.front();
j.pop() ;
return k;
}
if(j.empty())
{
ll k=i.front();
i.pop();
return k;
}
if( i.front() >j.front() )
{
ll k=j.front();
j.pop() ;
return k;
}
else
{
ll k=i.front();
i.pop();
return k;
}
}
ll lmax(ll i,ll j)
{
if(i<j)return j;
else return i;
}
int main()
{
scanf("%d%d%d",&n,&m,&seed);
generate_array(a,n,m,seed);
for(ll i=1;i<=ma;i++)while(t[i]--)q.push(i);//初始化结束 且 q内为升序数组
ll c,d;
while(q.size() || p.size()-1)
{
c=qmin(q,p);
d=qmin(q,p);
//printf("%lld %lld",c,d);
p.push( lmax(c*2,d) );
}
printf("%lld",p.front());
return 0;
}
`
亲测AC有效


浙公网安备 33010602011771号