P1077 [NOIP2012 普及组] 摆花
为什么以下这些代码都对?
二维,第二层循环升序
#include <bits/stdc++.h>
using namespace std;
int n, m, a[102], ans, dp[102][102]; //dp[i][j]表示前i种花,摆放j盆的方案数
int main()
{scanf("%d %d", &n, &m);for(int i=1; i<=n; ++i){scanf("%d", &a[i]);}dp[0][0]=1;for(int i=1; i<=n; ++i){ //n组背包,背包方案 for(int j=0; j<=m; ++j){ for(int k=0; k<=a[i]; ++k){if(j>=k)dp[i][j]=(dp[i][j]+dp[i-1][j-k])%1000007;}}}printf("%d", dp[n][m]);return 0;
}
二维,第二层循环倒序
#include <bits/stdc++.h>
using namespace std;
int n, m, a[102], ans, dp[102][102]; //dp[i][j]表示前i种花,摆放j盆的方案数
int main()
{scanf("%d %d", &n, &m);for(int i=1; i<=n; ++i){scanf("%d", &a[i]);}dp[0][0]=1;for(int i=1; i<=n; ++i){ //n组背包,背包方案 for(int j=m; j>=0; --j){ for(int k=0; k<=a[i]; ++k){if(j>=k)dp[i][j]=(dp[i][j]+dp[i-1][j-k])%1000007;}}}printf("%d", dp[n][m]);return 0;
}
二维,内层循环和第二层循环交换顺序
#include <bits/stdc++.h>
using namespace std;
int n, m, a[102], ans, dp[102][102]; //dp[i][j]表示前i种花,摆放j盆的方案数
int main()
{scanf("%d %d", &n, &m);for(int i=1; i<=n; ++i){scanf("%d", &a[i]);}dp[0][0]=1;for(int i=1; i<=n; ++i){ //n组背包,背包方案for(int k=0; k<=a[i]; ++k){for(int j=m; j>=0; --j){ if(j>=k)dp[i][j]=(dp[i][j]+dp[i-1][j-k])%1000007;}}}printf("%d", dp[n][m]);return 0;
}
一维,第二层循环倒序,内层循环k从1开始
#include <bits/stdc++.h>
using namespace std;
int n, m, a[102], ans, dp[102]; //dp[i][j]表示前i种花,摆放j盆的方案数
int main()
{scanf("%d %d", &n, &m);for(int i=1; i<=n; ++i){scanf("%d", &a[i]);}dp[0]=1;for(int i=1; i<=n; ++i){ //n组背包,背包方案 for(int j=m; j>=0; --j){ for(int k=1; k<=a[i]; ++k){if(j>=k)dp[j]=(dp[j]+dp[j-k])%1000007;}}}printf("%d", dp[m]);return 0;
}
为什么以下这些代码错误?
一维,第二层循环倒序,内层循环k从0开始
#include <bits/stdc++.h>
using namespace std;
int n, m, a[102], ans, dp[102]; //dp[i][j]表示前i种花,摆放j盆的方案数
int main()
{scanf("%d %d", &n, &m);for(int i=1; i<=n; ++i){scanf("%d", &a[i]);}dp[0]=1;for(int i=1; i<=n; ++i){ //n组背包,背包方案 for(int j=m; j>=0; --j){ for(int k=0; k<=a[i]; ++k){if(j>=k)dp[j]=(dp[j]+dp[j-k])%1000007;}}}printf("%d", dp[m]);return 0;
}
一维,第二层循环升序,内层循环k从1开始
#include <bits/stdc++.h>
using namespace std;
int n, m, a[102], ans, dp[102]; //dp[i][j]表示前i种花,摆放j盆的方案数
int main()
{scanf("%d %d", &n, &m);for(int i=1; i<=n; ++i){scanf("%d", &a[i]);}dp[0]=1;for(int i=1; i<=n; ++i){ //n组背包,背包方案 for(int j=0; j<=m; ++j){ for(int k=1; k<=a[i]; ++k){if(j>=k)dp[j]=(dp[j]+dp[j-k])%1000007;}}}printf("%d", dp[m]);return 0;
}
一维,k从1开始,j倒序,内层循环和第二层循环交换顺序
#include <bits/stdc++.h>
using namespace std;
int n, m, a[102], ans, dp[102]; //dp[i][j]表示前i种花,摆放j盆的方案数
int main()
{scanf("%d %d", &n, &m);for(int i=1; i<=n; ++i){scanf("%d", &a[i]);}dp[0]=1;for(int i=1; i<=n; ++i){ //n组背包,背包方案 for(int k=1; k<=a[i]; ++k){for(int j=m; j>=0; --j){ if(j>=k)dp[j]=(dp[j]+dp[j-k])%1000007;}}}printf("%d", dp[m]);return 0;
}
P1757 通天之分组背包
为什么下面代码正确?
#include <bits/stdc++.h>
using namespace std;
int n, m, t, dp[1001], w[1001][1001], v[1001][1001], num[1001];
int main()
{cin >> m >> n;for(int i=1; i<=n; ++i){int ww, vv, group;cin >> ww >> vv >> group;num[group]++;t=max(t, group);w[group][num[group]]=ww;v[group][num[group]]=vv;}for(int i=1; i<=t; ++i){ //组数for(int j=m; j>=0; --j){ //背包容量,因为每组只能选1个,所以逆序//枚举,倒序顺序都行for(int k=num[i]; k>=1; --k){ //这个不能放在第二层,否则k个物品中会同时选if(j>=w[i][k]){dp[j]=max(dp[j], dp[j-w[i][k]]+v[i][k]);}}}}cout << dp[m];return 0;
}
转成二维怎么转?下面的代码为什么只有99分
#include <bits/stdc++.h>
using namespace std;
int n, m, t, dp[1001][1001], w[1001][1001], v[1001][1001], num[1001];
int main()
{cin >> m >> n;for(int i=1; i<=n; ++i){int ww, vv, group;cin >> ww >> vv >> group;num[group]++;t=max(t, group);w[group][num[group]]=ww;v[group][num[group]]=vv;}for(int i=1; i<=t; ++i){ //组数for(int j=m; j>=0; --j){ //背包容量,因为每组只能选1个,所以逆序//枚举,倒序顺序都行for(int k=num[i]; k>=1; --k){ //这个不能放在第二层,否则k个物品中会同时选if(j>=w[i][k]){dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i][k]]+v[i][k]);}}}}cout << dp[t][m];return 0;
}
内层循环大于等于1为什么也能得99分
#include <bits/stdc++.h>
using namespace std;
int n, m, t, dp[1001][1001], w[1001][1001], v[1001][1001], num[1001];
int main()
{cin >> m >> n;for(int i=1; i<=n; ++i){int ww, vv, group;cin >> ww >> vv >> group;num[group]++;t=max(t, group);w[group][num[group]]=ww;v[group][num[group]]=vv;}for(int i=1; i<=t; ++i){ //组数for(int j=m; j>=1; --j){ //背包容量,因为每组只能选1个,所以逆序//枚举,倒序顺序都行for(int k=num[i]; k>=1; --k){ //这个不能放在第二层,否则k个物品中会同时选if(j>=w[i][k]){dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i][k]]+v[i][k]);}}}}cout << dp[t][m];return 0;
}
第二层循环改成升序,为什么也能得99分?
#include <bits/stdc++.h>
using namespace std;
int n, m, t, dp[1001][1001], w[1001][1001], v[1001][1001], num[1001];
int main()
{cin >> m >> n;for(int i=1; i<=n; ++i){int ww, vv, group;cin >> ww >> vv >> group;num[group]++;t=max(t, group);w[group][num[group]]=ww;v[group][num[group]]=vv;}for(int i=1; i<=t; ++i){ //组数for(int j=0; j<=m; ++j){ //背包容量,因为每组只能选1个,所以逆序//枚举,倒序顺序都行for(int k=num[i]; k>=1; --k){ //这个不能放在第二层,否则k个物品中会同时选if(j>=w[i][k]){dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i][k]]+v[i][k]);}}}}cout << dp[t][m];return 0;
}
去掉等号以后,为什么只有3分了
#include <bits/stdc++.h>
using namespace std;
int n, m, t, dp[1001][1001], w[1001][1001], v[1001][1001], num[1001];
int main()
{cin >> m >> n;for(int i=1; i<=n; ++i){int ww, vv, group;cin >> ww >> vv >> group;num[group]++;t=max(t, group);w[group][num[group]]=ww;v[group][num[group]]=vv;}for(int i=1; i<=t; ++i){ //组数for(int j=m; j>=0; --j){ //背包容量,因为每组只能选1个,所以逆序//枚举,倒序顺序都行for(int k=num[i]; k>=1; --k){ //这个不能放在第二层,否则k个物品中会同时选if(j>w[i][k]){dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i][k]]+v[i][k]);}}}}cout << dp[t][m];return 0;
}
把正确代码的内层和第二层交换以后,为什么98分?
#include <bits/stdc++.h>
using namespace std;
int n, m, t, dp[1001], w[1001][1001], v[1001][1001], num[1001];
int main()
{cin >> m >> n;for(int i=1; i<=n; ++i){int ww, vv, group;cin >> ww >> vv >> group;num[group]++;t=max(t, group);w[group][num[group]]=ww;v[group][num[group]]=vv;}for(int i=1; i<=t; ++i){ //组数//枚举,倒序顺序都行for(int k=num[i]; k>=1; --k){ //这个不能放在第二层,否则k个物品中会同时选for(int j=m; j>=0; --j){ //背包容量,因为每组只能选1个,所以逆序if(j>=w[i][k]){dp[j]=max(dp[j], dp[j-w[i][k]]+v[i][k]);}}}}cout << dp[m];return 0;
}
通天之分组背包中说:“每组中的物品相互冲突”,这句话在正确代码中是如何体现的?
摆花
“小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共 m 盆。通过调查顾客的喜好,小明列出了顾客最喜欢的 n 种花,从 1 到 n 标号。为了在门口展出更多种花,规定第 i 种花不能超过 ai盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。
试编程计算,一共有多少种不同的摆花方案。”,
摆花中第i种花可以摆1盆,两盆...ai盆,并非每种花只能摆一盆,为什么一维正确代码中,第二层循环仍然倒序,反而正序会出错?
这两道题难倒是一道题吗?
如果是请说明你的理解,如果不是请说明为什么摆花这道题一维做法的第二层循环不能升序
思考:摆花初始化dp[0][0]=1,或者dp[0]=1,为什么通天之分组背包不用初始化,而且初始化后只能得3分。
#include <bits/stdc++.h>
using namespace std;
int n, m, t, dp[1001], w[1001][1001], v[1001][1001], num[1001];
int main()
{cin >> m >> n;for(int i=1; i<=n; ++i){int ww, vv, group;cin >> ww >> vv >> group;num[group]++;t=max(t, group);w[group][num[group]]=ww;v[group][num[group]]=vv;}dp[0]=1;for(int i=1; i<=t; ++i){ //组数for(int j=m; j>=0; --j){ //背包容量,因为每组只能选1个,所以逆序//枚举,倒序顺序都行for(int k=num[i]; k>=1; --k){ //这个不能放在第二层,否则k个物品中会同时选if(j>=w[i][k]){dp[j]=max(dp[j], dp[j-w[i][k]]+v[i][k]);}}}}cout << dp[m];return 0;
}