题目背景

​uim​​​神犇拿到了​​uoi​​​的​​ra​​​(镭牌)后,立刻拉着基友​​小A​​到了一家……餐馆,很低端的那种。

​uim​​指着墙上的价目表(太低级了没有菜单),说:“随便点”。

题目描述

不过​​uim​​​由于买了一些​​辅(e)辅(ro)书​​,口袋里只剩M元(M≤10000)。

餐馆虽低端,但是菜品种类不少,有N种(N≤100),第iii种卖ai元(ai≤1000)。由于是很低端的餐馆,所以每种菜只有一份。

​小A​​​奉行“不把钱吃光不罢休”,所以他点单一定刚好吧​​uim​​身上所有钱花完。他想知道有多少种点菜方法。

由于​​小A​​肚子太饿,所以最多只能等待1秒。

输入输出格式

输入格式:

 

第一行是两个数字,表示N和M。

第二行起N个正数ai(可以有相同的数字,每个数字均在1000以内)。

 

输出格式:

 

一个正整数,表示点菜方案数,保证答案的范围在int之内。

 

输入输出样例

输入样例#1: 复制


4 4 1 1 2 2


输出样例#1: 复制

dp[i][j]表示前i个菜品恰好花费j元的方案数

然后我们来考虑如何转移:

首先,因为dp数组存储的是方案数,所以思路可以是它可以由那些方案转移过来

1.第一种可行的方案是买当前第i道菜品,这个时候前i-1个物品所需要的钱应该是j-a[i], 也就是说前i个物品中所有能凑出j-a[i]元的方案再加上当前这道菜品,就可以变成前i个物品所需的钱为j的方案数。即dp[i][j]+=dp[i-1][j-a[i]]

2.不买当前第i道菜品,这时候,也就是前i-1个物品凑成j的方案,即dp[i][j]+=dp[i-1][j];

注意这里是方案的叠加,因为每一种方案都是可行的。

你可能以为现在已经结束了,但实际上你还没有考虑一种方案,当前第i种物品恰好为j元钱,所以可以只买它自己。这种情况其实包含在1中,但是由于dp[i][0]=0,所以不会算入这种情况。所以我们要把所有的dp[i][0]更新成1,这样就可以计算上面所述的那种情况。

 

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 110
using namespace std;
int n,m,a[N],dp[N][10010];
int main()
{

scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=0;i<=n;i++) //注意这里要从0开始
dp[i][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
dp[i][j]+=dp[i-1][j];
if(j>=a[i])
dp[i][j]+=dp[i-1][j-a[i]];
}
cout<<dp[n][m];
return 0;
}

优化后

 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std ;
const int MAX = 51000;

int a[MAX] ;
int dp[MAX] ;
int main()
{
int n , m ;
cin >>n >> m ;
for(int i = 1 ;i<=n ; i++ ) cin >> a[i] ;
dp[0] = 1 ;
for(int i = 1 ; i<=n ; i++ ){
for(int j = m ;j>=a[i];j--){
dp[j]+=dp[j-a[i]] ;
}
}
cout<<dp[m];
return 0 ;
}

搜索

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <set>
#include <cstring>
using namespace std ;
const int MAX = 51000;

int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int vis[MAX] ;
int a[MAX] ;
int ans = 0 ;
int n , m ;
void dfs(int v ,int k ){
if(v>m) return ;
if(v== m){
ans++ ;
return ;
}
for(int i = k+1 ; i<=n ; i++ ){
if(!vis[i]){
vis[i] = 1 ;
dfs(v+a[i],i);
vis[i] = 0 ;
}
}

return ;
}
int main()
{

cin >> n >> m ;
for(int i = 1 ; i<=n ; i++ ){
a[i] = read() ;
}
dfs(0,0);
cout<<ans;
return 0 ;
}