经典回顾(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hqh131360239/article/details/82875827

依稀的记得河南省第七届ACM省赛的第一题《物资调度》,貌似可以用dfs水过,但是后来发现可以用dp解决。现在在做面试题的时候又遇到了类似的问题。突然发现这题原来也不过如此,我也能把动态转移方程给推出来了。

方法一:dfs,每个数都有取和不取两种状态。如果数据太大(2^n),一定会超时。

/*#include<iostream>
using namespace std;
int n,m;
int ans=0;
void dfs(int i,int sum){
    if(sum==m){
        ans++;
        return;
    }
    if(i>n) return;
    dfs(i+1,sum+i);
    dfs(i+1,sum);
}
int main()
{
    cin>>n>>m;
    dfs(1,0);
    cout<<ans<<endl;
}*/
/**区别两种方法,一个传参,一个不传参*/
#include<iostream>
using namespace std;
int n,m;
int sum=0;
int ans=0;
void dfs(int i){
    if(sum==m){
        ans++;
        return;
    }
    if(i>n) return;
    sum+=i;
    dfs(i+1);
    sum-=i;
    dfs(i+1);
}
int main()
{
    cin>>n>>m;
    dfs(1);
    cout<<ans<<endl;
}

方法二:dp,先回顾一下01背包,dp[i][j]=(dp[i-1][j],dp[i-1][j-w]+v),dp[i][j]表示前i个物品在背包容量为j的时候最大价值,动态转移方程是不放和放入那个所容纳的价值最大。(注意每个物品是无限的)

注意不能定义为一维的dp,至少要定义为一个滚动dp,因为使用的是上一行覆盖前的数据。这里的数据只能使用一次,不是传统01背包,物品无限个。

dp[i][j]表示前i个数据和为j的组合次数。首先每次增加的i都是从j=i开始影响后面的组合情况。j表示和,i表示增加的值。小于j的直接继承上一行,等于j的时候只能在原基础上影响多一次,大于j的时候只能在与基础加上j-i这个容量的次数。

dp[i][j]= dp[i-1][j]+dp[i-1][j-i];

if(i==j)  dp[i][j]+=1;

#include<iostream>
using namespace std;
int main(){
    int n,m;
    int dp[125][125]={0};
    int sum=0;
    cin>>n>>m;
    //依次放入n个物品
    for(int i=1;i<=n;i++){
        for(int j=1;j<i;j++)   //不影响的
            dp[i][j]=dp[i-1][j];
        for(int j=i;j<=m;j++){ //能够影响的
            dp[i][j]=dp[i-1][j]+dp[i-1][j-i];
            if(i==j)   dp[i][j]+=1;   //i==j时,dp[i-1][j-i]=0,其实可以多影响一次
        }
    }
    cout<<dp[n][m]<<endl;
}

猜你喜欢

转载自blog.csdn.net/hqh131360239/article/details/82875827