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

题目描述

现有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
#include
#include
#include
#include
#include
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 1
1 2 2
*/

你可能感兴趣的:(————单个题目———,————DP————,————搜索————,DP——背包)