今天这题是一个非常不错的思维题,贪心专业户的我表示非常满足。
题目链接:题目
题意:将n个数字拆分成k个,比如5可以拆分为(1+1+2)变成3个。拆完之后求拆出来的所有数字的平方和最小为多少
思路:首先进行一个简单的证明,对于任意一个正整数,将他拆分为小于等于他本身的个数个数字之后的最小平方和必然是尽量平均之后的结果,这个可以用平方平均值很快看出来。那么就可以O(1)算出来他的贡献。其次,我们进行贪心,假设我们已经对将当前的数字拆分为了q个数字,其中第i个数字被分为了Ai个(其中初始状态全部为1)。那么就有这样的一个贪心法,对于每一个数字再拆分一次(如果可以拆的话),均有一个差值,也就是将一个数字拆为ai个数字与ai+1个数字的差。我们令每次操作这个差值最大,就可以尽量减少最后的总和
代码如下
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const ll inf = 0x3f3f3f3f3f3f3f3f;
struct num
{
ll x;
ll times;
num() { times = 1; }
ll div(int v) const
{
v+=times;
ll t = x / v;
ll b = x - t * v;
return b * (t + 1) * (t + 1) + (v - b) * t * t;
}
bool operator<(const num &other) const
{
return div(0) - div(1) < other.div(0) - other.div(1);
}
};
ll n, k;
priority_queue<num, vector<num>, less<num> > que;
int main()
{
cin >> n >> k;
for (int i = 1; i <= n; i++)
{
num v;
cin >> v.x, que.push(v);
}
for (int i = 1; i <= k - n; i++)
{
num q = que.top();
q.times++;
//cout<<q.x<<" "<<q.times<<endl;
que.pop();
que.push(q);
}
ll ans = 0;
while (!que.empty())
{
ans += que.top().div(0);
//cout<<que.top().div(0)<<" ";
que.pop();
}
cout << ans << endl;
}
错误记录:第一次贪心策略出错,我将每次贪心拆出来的数字都放回队列,每次找最大的那个数字拆,容易举反例,一个100拆分为三个数字,第一次拆成50,50,第二次拆成25,25,50,这必然没有33,33,34小。第二次是因为第一次错误之后换了一个思路dp了一次,WA之后迅速否决,自重思路正确了