题目
知识点: 背包DP
讲解:
这一题不算太难,可以说是送分题。
我们考虑最多要选的肯定只有那n个嘛。
然后我们继续考虑,一个货币不需要用当且仅当这个货币可以被已经选了的货币表示出来,这样这个就是完全背包了。
我们定义dp[i]表示i这个货币能否被表示出来,1表示可以,0表示不可以。
我们对于一个货币,如果它不能被任何一个货币表示出来,那么我们就必须要选它,而如果可以我们就不用选,假设我们的货币目前为i,我们枚举所有比i小的x,看一下x和i-x是否同时满足。这样可是可以,但是会超时,我们考虑对于一个货币i可以组成它的必定是比它小的,所以我们先从小到大排序,对于目前的货币i,如果dp[i]==0,那么我们肯定要选它,而dp[i]==1的话我们肯定不选,在考虑怎么更新别人,我们暴力枚举j为由i到最大的货币max,如果dp[j-i]==1的话j一定可以被表示,解释一下,如果i和j-i都能被表示,那么j一定可以被表示,因为我们现在dp到了i,如果i本来就可以被表示,那么万事大吉,如果不可以呢?
我们一定要选它,那么它就可以被自己表示了,所以等后面更新时i一定可以被表示,所以我们只需要考虑j-i就好了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[25100],f[200];
bool cmp(int x,int y)//从小到大
{
return x<y;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)//多组数据
{
memset(dp,0,sizeof(dp));//清空
memset(f,0,sizeof(f));
int n,ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&f[i]);//输入
sort(f+1,f+1+n,cmp);//排序
dp[0]=1;//0一定可以被表示,这是边界
for(int i=1;i<=n;i++)
{
if(dp[f[i]]==0)//对于这个货币不能被表示
ans++;//选他
for(int j=f[i];j<=f[n];j++)
dp[j]=dp[j]|dp[j-f[i]];//无论j和j-f[i]两个哪个可以被表示,j都能被表示
}
printf("%d\n",ans);//输出答案
}
return 0;
}
这就是day1 T2,很简单吧 (只要能看出来)。
如果有看不懂的欢迎留言。