红鲤鱼与绿鲤鱼与驴
今天把昨天学的背包问题的练习做了一下
- AcWing278 数组组合
https://ac.nowcoder.com/acm/contest/1042/A
1、题意:在N个数中找出其和为M的若干个数。先读入正整数N(1<N<100)和M(1<M<10000), 再读入N个正数(可以有相同的数字,每个数字均在1000以内),在这N个数中找出若干个数, 使它们的和是M,把满足条件的数字组合都找出来以统计组合的个数,输出组合的个数(不考虑组合是否相同)。
2、思路:典型的01背包问题,n个整数看作n个物品,m看作背包容量,在外层循环到i时,设f[j]表示“和为j”时有多少种方案。把原来的max改成求和就行了
3、普通代码:
#include<iostream> using namespace std; int a[105]; int f[105][10005]; int main(){ int n,m; cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; f[0][0]=1; for (int i = 1; i <= n; i++){ for (int j = m; j >= 0; j--) f[i][j] += f[i - 1][j]; for (int j = m; j >= a[i]; j--) f[i][j] += f[i - 1][j - a[i]]; } cout<<f[n][m]<<endl; return 0; }
4、进阶代码:(昨天学的优化)
#include<iostream> using namespace std; int a[105]; int f[10005]; int main(){ int n,m; cin>>n>>m; for(int i=0;i<n;i++) cin>>a[i]; f[0]=1; for(int i=0;i<n;i++) for(int j=m;j>=a[i];j--) f[j]+=f[j-a[i]]; cout<<f[m]<<endl; return 0; }
- AcWing279 自然数拆分
https://ac.nowcoder.com/acm/contest/1042/B
1、题意:给定一个自然数N,要求把N拆分成若干个正整数相加的形式,参与加法运算的数可以重复。求拆分的方案数 mod 2147483648 拆分的方案数 mod 2147483648 拆分的方案数mod2147483648的结果。1≤N≤4000
2、思路:经典的完全背包问题,物品就是自然数 1 ~ n-1,背包容量是n,把原来的max改成求和就行了,记得 mod 2147483648
3、代码:
#include<iostream> using namespace std; int f[4005]; int main() { int n; cin>>n; f[0]=1; for(int i=1;i<n;i++) for(int j=i;j<=n;j++) f[j]=(f[j]+f[j-i])%2147483648u; cout<<f[n]; return 0; }
前两道难道还可以 最后一道压轴题啃了半天
- AcWing280 陪审团
英文版:https://ac.nowcoder.com/acm/contest/1042/C
中文版:https://www.acwing.com/problem/content/282/
书和视频看了好几遍 有点郁闷
题意和思路就不写了 直接上代码
跟着yxc大佬的视频 打了代码和注释
这两天还要再多看两遍 争取完全理解
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; const int N=205; const int M=805; //从-400到400 一共801中情况 const int base=400; //将得分的差看作"体积" 可能是负数 所以加一个偏差值 int p[N],d[N]; int f[N][25][M]; int ans[N]; int main() { int T = 1; int n,m; while (scanf("%d%d", &n, &m), n || m) { for (int i = 1; i <= n; i ++ ) scanf("%d%d", &p[i], &d[i]); memset(f, -0x3f, sizeof f); f[0][0][base] = 0; //base表示差值为0 for (int i = 1; i <= n; i ++ ) for (int j = 0; j <= m; j ++ ) for (int k = 0; k < M; k ++ ) { f[i][j][k] = f[i - 1][j][k]; //不选择i的状态 int t = k - (p[i] - d[i]); //计算差值 判断选择i的状态 是否成立 if (t < 0 || t >= M) continue; //判断差值是否在范围内 if (j < 1) continue; //选择i了所以j>1 f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][t] + p[i] + d[i]); } int v = 0; while (f[n][m][base - v] < 0 && f[n][m][base + v] < 0) v ++ ; //找出最小的差值 if (f[n][m][base - v] > f[n][m][base + v]) //判断正负哪种和更大 v = base - v; else v = base + v; int cnt = 0; int i = n, j = m, k = v; while (j) //还没有找够人 { if (f[i][j][k] == f[i - 1][j][k]) //相等则可以不选 i -- ; else //选择即更新状态 { ans[cnt ++ ] = i; k -= (p[i] - d[i]); i --; j --; } } int sp = 0, sd = 0; //求和 for (int i = 0; i < cnt; i ++ ) { sp += p[ans[i]]; sd += d[ans[i]]; } printf("Jury #%d\n", T ++ ); printf("Best jury has value %d for prosecution and value %d for defence:\n", sp, sd); sort(ans, ans + cnt); for (int i = 0; i < cnt; i ++ ) printf(" %d", ans[i]); puts("\n"); //两个回车 } return 0; }
自言自语Part:
本来计划上午做四道题 下午继续学新东西 结果下午又忙屁孩小升初和新闻稿的事了
可能这就是传说中计划赶不上变化吧 不过晚上回来还是坚持把最后一道题做了
并且坚定的拒绝了强哥抛来的打游戏的橄榄枝 哎 他们又要说我放他们鸽子了
其实后面还有一道抛硬币的题 不过一看到难度是困难 果断撤退 以后有机会了把它在补上吧
Good Luck! 今天只能算0.5咯