思路分析:
我一开始没往背包那想,我想的是dp[i][j]
表示 i 值 剩余 j 次分解位的方案总数。
第一层 for 遍历 i 值 (1-32768)
第二层for 遍历 j 分解位个数 (0-4)
第三层for 遍历 k 平方数 (1-sqrt(i))
dp[i][j] = dp[i-k*k][j-1]
这样写理论上可以实现,但是无法去重。
假设i=5, j=1
,k 可以取 1, 2. k取1就由dp[4][0]
推来,k取2就由dp[1][0]
推来。这样就包含了[1,2]和[2,1]两种情况。
后来看了题解才发现是一个二位费用完全背包问题。
多少个平方数就表示有多少种物品,给出的n表示背包容量。
每个平方数可选无限次,每个平方数 x 消耗 x*x 体积和 1 个分解位。
可以定义dp[i][j][k]
表示前 i 种平方数,剩余总和 j,剩余选择 j 的方案总数。
dp[i][j][k] += dp[i-1][j-i*i][k-1]
通过完全背包可以除去 i 那一维。
经验:以后看dp,凡是看到决策涉及到选与不选,选多个这种题型都先往背包方向考虑一下。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
//当成二维费用完全背包问题
//有多少个平方数就表示有多少种物品,给出的n表示背包容量
//每个平方数可选无限次,每个平方数x消耗x*x体积和1个分解位
int main()
{
int N = 32768;
long long dp[32770][5]; //dp[j][k]表示剩余容量j,剩余分解位k的方案数
memset(dp,0,sizeof(dp));
for(int i = 0;i <= 4;i++) dp[0][i] = 1; //容量为0时每个剩余分解位的方案数都考虑进去
for(int i = 1;i*i <= N;i++) //因为平方数按顺序遍历,所以自然就去重了
{
for(int j = i*i;j <= N;j++)
{
for(int k = 1;k <= 4;k++)
{
dp[j][k] += dp[j-i*i][k-1];
}
}
}
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
printf("%lld\n",dp[n][4]);
}
return 0;
}