洛谷 1441 砝码称重 搜索+DP 解题报告

来源:互联网 发布:cs1.6中文版for mac 编辑:程序博客网 时间:2024/05/22 15:09

题目描述

现有n个砝码,重量分别为a1,a2,a3,……,an,在去掉m个砝码后,问最多能称量出多少不同的重量(不包括0)。

输入输出格式

输入格式:

输入文件weight.in的第1行为有两个整数n和m,用空格分隔

第2行有n个正整数a1,a2,a3,……,an,表示每个砝码的重量。

输出格式:

输出文件weight.out仅包括1个整数,为最多能称量出的重量。

输入输出样例

输入样例#1:
3 1
1 2 2
输出样例#1:
3

说明

【样例说明】

在去掉一个重量为2的砝码后,能称量出1,2,3共3种重量。

【数据规模】

对于20%的数据,m=0;

对于50%的数据,m≤1;

对于50%的数据,n≤10;

对于100%的数据,n≤20,m≤4,m<n,ai≤100。

思路

首先,对于这种状态类型的题目,数据范围比较小,我们可以dfs暴力枚举每一种情况,然后在每一种情况确定下来过后,对其进行01背包的操作。
通过dfs过程找到一种状态以后,求出使用当前留下的这些砝码可以凑出多少个不同的重量,我们通过dp解决这个问题。
定义dp[i][j]为当前选取到了第j个砝码,如果通过之前的砝码可以称量出重量i那么dp[i][j]的值为1。
状态转移方程为: dp[i][j]=dp[iw[i]][j1]
初始状态为dp[0][j]=1
最后dp[i][n]中1的个数就是通过这些砝码可以计算出的重量值。
但是因为只求每次的最大值,所以只用一维的数组滚动,每次清零就好了。可以只定义一个dp[i]数组,从而降低了时间空间复杂度,但是要注意此时内层循环倒序。(想想为什么)
这里要注意,我在dfs的时候第一次只是u==n就进入判断然后背包了。但是实际上是u==n+1

代码

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>#include<vector>using namespace std;const int N=20+2;const int M=2000+5;int n,m,w[N],dp[M],flag[N],top,ans=0,tot;void work01(){    memset(dp,0,sizeof(dp));    dp[1]=1;tot=0;top=1;    for (int i=1;i<=n;i++)    {        if (flag[i]==1) continue;        for (int j=top;j>=0;j--)        if (dp[j]&&!dp[j+w[i]]) dp[j+w[i]]=1,tot++;        top+=w[i];    }//滚动     ans=max(ans,tot);}void dfs(int u,int now){    if (now>m) return ;    if (u==n+1) {if (now==m) work01();return ;}    dfs(u+1,now);    flag[u]=1;    dfs(u+1,now+1);    flag[u]=0; } int main(){    scanf("%d%d",&n,&m);    for (int i=1;i<=n;i++)    scanf("%d",&w[i]);    dfs(1,0);    printf("%d\n",ans);    return 0;}/*3 11 2 2*/
原创粉丝点击