洛谷 P1164:小A点菜(DP/DFS)

题目背景

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

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

题目描述

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

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

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

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

输入输出格式

输入格式:

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

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

输出格式:

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

输入输出样例

输入样例#1: 复制

4 4
1 1 2 2

输出样例#1: 复制

3

思路

  • DP:好像是01背包的变形(看到好多大佬都是这样说)。用dp数组来记录拿到总价值数为j时的方案数(我是这样理解的,把dp过程打表出来好像也是这样),最后输出dp[m]的方案数就行了。下面是我打的dp过程时的表(数据是样例中的数据)

  • DFS:暴力似乎能解决一切问题(在时间够的情况下)。直接枚举所有的情况,加上剪枝就可以了(这个题加上剪枝并没有减少什么时间,难道是剪枝的姿势不对?)

AC代码

DP:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <limits.h>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <set>
#include <string>
#define ll long long
#define ms(a) memset(a,0,sizeof(a))
#define pi acos(-1.0)
#define INF 0x3f3f3f3f
const double E=exp(1);
const int maxn=1e3+10;
using namespace std;
int a[maxn];
int dp[maxn];//记录拿到总价值为j的物品的方案数
int main(int argc, char const *argv[])
{
	ios::sync_with_stdio(false);
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	ms(dp);
	dp[0]=1;
	for(int i=1;i<=n;i++)
	{
		// cout<<"取到第"<<i<<"个物品时"<<endl;
		for(int j=m;j>=a[i];j--)
		{
			dp[j]+=dp[j-a[i]];
			// cout<<"i:"<<i<<"	"<<"j:"<<j<<"	"<<"a[i]:"<<a[i]<<"	"<<"dp[j]:"<<dp[j]<<endl;
		}		
	}
	cout<<dp[m]<<endl;
	return 0;
}

DFS:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <limits.h>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <set>
#include <string>
#define ll long long
#define ms(a) memset(a,0,sizeof(a))
#define pi acos(-1.0)
#define INF 0x3f3f3f3f
const double E=exp(1);
const int maxn=1e6+10;
using namespace std;
int n,m;
int ans;
int a[maxn];
int vis[maxn];//记录这种菜是否选过
void dfs(int x,int y)//x表示选到第几个菜,y表示当前菜的总价值
{
	if(y>m)
		return ;
	if(y==m)
	{
		ans++;
		return ;
	}
	// 从当前所选的菜的位置向后遍历
	for(int i=x+1;i<=n;i++)
	{
		// 如果当前菜没有被选
		if(vis[i]==0)
		{
			vis[i]=1;
			dfs(i,y+a[i]);
			// 回溯
			vis[i]=0;
		}
	}
	
}
int main(int argc, char const *argv[])
{
	ios::sync_with_stdio(false);
	cin>>n>>m;
	ms(vis);
	ms(a);
	for(int i=1;i<=n;i++)
		cin>>a[i];
	ans=0;
	dfs(0,0);
	cout<<ans<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/Friends-A/p/10324405.html