蓝桥杯2021年省赛,编程题(C++)
这道题主要考察了基础的动态规划思想
你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1, W2,⋅⋅⋅, WN。
请你计算一共可以称出多少种不同的重量? 注意砝码可以放在天平两边。
输入的第一行包含一个整数 N。
第二行包含 N 个整数:W1,W2,W3,⋅⋅⋅,WN。
输出一个整数代表答案。
3 1 4 6
10
能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11。
1=1;
2 = 6 − 4 (天平一边放 6,另一边放 4);
3=4−1;
4=4;
5=6−1;
6=6;
7=1+6;
9=4+6−1;
10=4+6;
11=1+4+6。
对于 50%的评测用例,1≤N≤15。
对于所有评测用例,1≤N≤100,N个砝码总重不超过 100000。
我们对于拿过来的每一种砝码,有两种选择,一种是放,另一种是不放。放的话,其实又有两种选择,一种是放到左边,一种是放到右边,但是注意,放到左边和右边的情况是不一样的。试想,现在天平上左边有1kg , 5kg两个重量的砝码,而天平的右边现在没有砝码,那么我取来一个重7kg的砝码,先选择放到左边,现在重量是1+5+7=13,而假如我放到右边呢,其实最终可以称出来的重量是abs(1+5-7)即1,因此这也引出了解决本题的dp思路。
我们建立dp[i][j]的过程及含义如下:
dp[i][j]表示在当前 i 种砝码下,对于重量 j 来说,是否能够拼凑出来,即dp的值只有0和1两种,1表示能够拼凑出来,0表示不能够拼凑出来。那么对于第 i 个砝码来说,重量为w[i],那么对于某一重量 j ,如果dp[i-1][j]==1,即在前面的砝码已经被遍历的情况下,可以拼凑出 j 这个重量,那么我们有:
dp[i][j]=dp[i-1][j] //这种情况是当前这个砝码选择不放
dp[i][j+w[i]]=1; //这种情况是当前这个砝码放到左边,比如(左:15,右5;实际上可以等于做10,右0)
dp[i][abs(j-w[i])]; //这种情况是当前这个砝码放到右边
思路大致是这样的,下面来看具体代码
#include<bits/stdc++.h> using namespace std; int dp[105][100005]; int w[150]; int main() { int n; cin>>n; int sum=0; for(int i=0;i<n;i++) { cin>>w[i]; sum+=w[i]; } sort(w,w+n); //将砝码从小到大排列 for(int i=0;i<n;i++) { for(int j=0;j<=sum;j++) { dp[i][j]=0; } } dp[0][w[0]]=1; //第一个砝码先赋初值 for(int i=1;i<n;i++) //遍历砝码的次序 { dp[i][w[i]]=1; //当前重量一定可以称出,即只放这一个砝码 for(int j=1;j<=sum;j++) //对于每一种重量,分别判断 { if(dp[i-1][j]==1) //之前能凑出来这种重量 { dp[i][j]=1; dp[i][abs(j-w[i])]=1; dp[i][j+w[i]]=1; } } } int ans=0; for(int i=1;i<=sum;i++) { if(dp[n-1][i]==1) { ans++; } } cout<<ans<<endl; return 0; }