Educational Codeforces Round 164 (Rated for Div. 2) D
因为理解错了题目导致我没错出来。我理解为有两个人取球,每个人每次都是取一组,也就是一组的球必须要放在一个人手里。。因为我之前做的一个背包是这个样子的。这就导致了,我认为每次转移所需要的信息是比实际要的多很多的,直接导致我没法设计一个正常的dp。
然后炸了。。。
好烦。。。完全可以上1800的一场,结果因为这种题意理解的问题寄了。
掉了25分。
这个其实也是因为之前做到的题目导致的吧。。给我的思维带来了一个奇妙的导向。很蠢。
以后其实遇到了这种完全没思路,但是大家又都会做的时候,可以把思维的每一个环节都仔细考虑一下,是不是题读错了之类的错。。。
太傻了。
其实就是背包,然后随便推推就没了。思路就是之前的那些,找到一个东西,固定它能够使得代价可统计。就没了。
然后因为取模的原因死在了test13,又因为下取整的原因死在了test5,这个下取整一定要带括号。。。不然会变化的。
具体做法就是先让\(a[i]\)从小到大排序,设\(f[i][j]\)表示前\(i\)类物品,任意取,最终数量有\(j\)个的方案数。
然后转移就是\(f[i][j]=f[i-1][j]+f[i][j-a[i]](0\leq j \leq tot),其中tot表所有球的数量和\)
然后我们从1枚举到n,枚举这次把哪一类小球产生的价值记入答案,因为我们的\(a[i]\)是从小到大排序的,所有每次加入的肯定都是当前这个方案里面数量最多的小球种类。
我们考虑怎么统计答案(其实这个应该是要先考虑,然后才有的这个做法),对于每一个种类的小球,我们枚举前面已经选取的多少个小球,设这个数字为\(sum\),而\(a[i]\)就是小球数量最多的种类的数量,当\(a[i]\geq sum\)的时候,产生的代价就是\(a[i]\times方案数\),而当\(a[i]<sum\)的时候,答案其实就是\((a[i]+sum)/2上取整 \times 方案数\),可以证明,如果答案不是这个,就说明\(a[i]\)前,也就是\(a[1...(i-1)]\)里面有比\(a[i]\)更大数字,而我们排过序了,这个是不可能的。所以是一定成立的。那其实就是一个计数背包。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read() {
char c=getchar();ll a=0,b=1;
for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
ll n,a[5001],f[5001][5001];
const ll Mod=998244353;
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
// ll T=read();
// while(T--)
// {
n=read();ll all=0;ll ans=0;
for(ll i=1;i<=n;i++)a[i]=read(),all+=a[i];
sort(a+1,a+1+n);
f[0][0]=1;
for(ll i=1;i<=n;i++)
{
for(ll j=0;j<=all;j++)
{
f[i][j]=(f[i-1][j]);
if(j>=a[i])f[i][j]+=f[i-1][j-a[i]];
f[i][j]%=Mod;
}
}
for(ll i=1;i<=n;i++)
{
for(ll j=0;j<=all;j++)
{
if(f[i-1][j]!=0)
{
if(j<=a[i])
{
ans+=a[i]*f[i-1][j];
}
else
{
ans+=f[i-1][j]*((j+a[i]+1)/2);
}
ans%=Mod;
}
}
}
cout<<ans%Mod<<endl;
// }
return 0;
}
/*
5
6 6 19 25 44
*/