NC17193 简单瞎搞题
状态表示:\(f[i][j]\)表示前\(i\)个数是否能组成数\(j\)
状态转移:枚举\(x_i\)∈\([l_i,r_i]\)
时间复杂度最坏为:\(10^{10}\),显然不能接受
空间复杂度为:\(100 M\),可采用滚动数组优化
下面针对时间复杂度优化:
\(f[i][j]\) 为前\(i\)个数字能否构成j,考虑第\(i\)个数选还是不选。
显然如果\(f[i-1][j-x[i]^2]==1\)的话\(f[i][j]\)就是可以的,\(x[i]\)的取值是\(l[i]\)到\(r[i]\)。
这样的话其实是有点浪费的——f数组是一个只有01两个值的数组,哪怕表示成bool类型都有点多余。于是我们可以考虑用bitset来优化它。
bitset你可以理解为一个长度很长的01数字串(实际上它是用int拼接而成),也可也理解为可以用位运算的bool数组。
这样的话一行就可以一起求——如果我们用\(f[i]\)来表示第i行的01串那么:\(f[i]=f[i] | (f[i-1]<<(x[j]^2))\)
然后bitset自带一个求1的个数的函数count,这就非常美滋滋了。
特别说明:bitset内部实现是用int拼接而成所以时间复杂度是\(O(长度/32)\)的,那么本题的时间复杂度是\(O(n^5/32)\)(bitset最大长度也就是背包体积是\(n^3\),还要枚举n,枚举从\(l_i\)到\(r_i\))
const int N=110,M=1e6+10;
int l[N],r[N];
bitset<M> f[N];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>l[i]>>r[i];
f[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=l[i];j<=r[i];j++)
f[i] |= f[i-1]<<(j*j);
cout<<f[n].count()<<endl;
//system("pause");
}
滚动数组优化:注意每轮清空
const int N=110,M=1e6+10;
int l[N],r[N];
bitset<M> f[2];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>l[i]>>r[i];
f[0][0]=1;
for(int i=1;i<=n;i++)
{
f[i&1].reset();
for(int j=l[i];j<=r[i];j++)
f[i&1] |= f[i-1&1]<<(j*j);
}
cout<<f[n&1].count()<<endl;
//system("pause");
}