Codeforces Round #517 C.Cram Time
这道题当初还以为是什么高深莫测的算法,dp还是搜索剪枝,没想到居然是道普普通通的贪心+模拟。这都没看出来,服了。
这道题错在两个地方:
- 数学推导有问题。我推导到了n(n+1)/2<=(a+b),但是接下来犯错了。我没有继续推导n最大时的不等式,而是想当然的计算了floor(sqrt(2*(a+b))),因为我觉得n和n+1不就相差1嘛,稍微大一点,开根号舍去小数位数不就完了嘛。但是还真不是这样的。当n最大时,设为N,我们只有不等式推导的N<=sqrt(2(a+b))<=N+1,而不能确定开方得数到底和N差几,可能差小于1,那向下取整对,也可能差大于1,那向下取整就错(如10亿)。这时候,就要加个循环,不断向下减一即可。
以后一定记住,求n(n+1)<x的最大整数n,向下取整后,还要有一步循环相减,不管这一步是否有效
- 算法设计有问题。讲真,虽然上面的式子我很早就推出来了,但是我一直不知道该拿什么策略填。没想到正解的策略这么的简单,简单到弱智。。。还是脑子不灵光啊。其实因为我没有意识到,既然可以确定能够填充的最长耗时学案编号k,那就可以确定,所有的从耗时1~k的所有学案,一定都可以填到这两天里,只是如何填的策略问题。正确策略是,从学案耗时k开始填,k,k-1,k-2,...2,1,每一个都填到两天中能填进去的那天,就这么简单。。。
#include <bits/stdc++.h>
using namespace std;
vector<int> va,vb;
int main() {
int a,b;
cin>>a>>b;
int n=sqrt(2.0*(a+b));
while (n*(n+1)/2>(a+b)) n--;
//cout<<"n=\t"<<n<<endl;
for (int i=n;i>=1;i--) {
if (a>=i) {a-=i;va.push_back(i);}
else vb.push_back(i);
}
cout<<va.size()<<endl;
if (!va.empty()) cout<<va[0];
for (int i=1;i<(int)va.size();i++)
cout<<' '<<va[i];
cout<<endl;
cout<<vb.size()<<endl;
if (!vb.empty()) cout<<vb[0];
for (int i=1;i<(int)vb.size();i++)
cout<<' '<<vb[i];
cout<<endl;
return 0;
}