转自yyr博客(https://www.luogu.org/blog/yeyangrui/)
(主要是想收录他的)
做这道题之前建议先看一下P2347这一道题,总体思路与这一道题十分相似(这一道题主要是多了一个DFS的过程),下面来说一下这道题(敲黑板):
使用的算法:DFS+DP(看到数据范围这么小应该首先想到的就应该是DFS)
做法:首先通过DFS枚举不取哪M个砝码的情况,再把每种情况依次DP,用一个ma记录最大值(详情见代码部分)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,a[21],f[2001],b[21],last=0,num=0,ma=0;
void search(int k)
{
if(k==m+1)//当取出了M个砝码时开始计算
{
memset(f,0,sizeof(f));//清零!!!这一步不能忘掉
f[0]=1;//当重量为0时要记为有一种情况,不然结果永远是0
for(int i=1;i<=n;i++)
{
if(b[i]==0)//如果这个砝码没有被拿走
{
for(int j=2000;j>=0;j--)//必须倒叙!不然可能f[4]用了4g的砝码,f[8]的时候又把4g的砝码拿出来用一遍
{
if(j+a[i]<=2000&&f[j]!=0)//如果这个重量可以取到且加上新砝码后重量小于等于最大重量(max_n*max_a[I]=2000)
{
f[j+a[i]]=1;//标记加上砝码后的重量也可以取到
}
}
}
}
num=0;
for(int i=1;i<=2000;i++)//看哪些重量可以取到
if(f[i])
num++;
if(num>ma)//记录最大值
ma=num;
return;
}
for(int i=last+1;i<=n;i++)//枚举取砝码的情况(这里采用的是顺次枚举,减少耗时)
{
b[i]=1;//记录为取过了
last=i;//记录取到哪一个了
search(k+1);//接着往下取
b[i]=0;//回溯
}
}
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];//读入
search(1);//枚举每种情况
cout<<ma;
return 0;
}