关于dp做法的一点...理解QAQ!!很浅显的 hdu1723Distribute Message(第一个独立做出来的dp题...大水题) 三种做法 暴力递归+记忆化搜索+dp

3 篇文章 0 订阅

Distribute Message

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2276    Accepted Submission(s): 1144


 

Problem Description

The contest’s message distribution is a big thing in prepare. Assuming N students stand in a row, from the row-head start transmit message, each person can transmit message to behind M personals, and how many ways could row-tail get the message?

 

 

Input

Input may contain multiple test cases. Each case contains N and M in one line. (0<=M<N<=30)
When N=0 and M=0, terminates the input and this test case is not to be processed.

 

 

Output

Output the ways of the Nth student get message.

 

 

Sample Input

 

4 1

4 2

0 0

 

 

Sample Output

 

1

3

 

Hint

4 1 : A->B->C->D 4 2 : A->B->C->D, A->C->D, A->B->D

可以说是很水的一道题目了 但是看了一个关于dp的视频之后终于对dp有点理解了 之前对dp一头雾水不知道从哪里下手

一直搜题解报告动态转移方程才能敲出来这样子

直到看了三个题解之后我看了一个视频 (我查了查是七月在线的动态规划实战课,也不是很贵)

dp定义啊之类的网上很多 就不再copy了 我就说说dp的做法吧(也是刚看的视频教程里面说的)

1.先找到最暴力的解法,然后去除冗余(这里我就用最暴力的方法写了一个递归,从上到下的(n到1),感觉从下到上(1到n)很难找到那个关系)

2.设计并存储状态(一维,二维,三维数组,map,其实这里我刚做这个题的时候并没有想到要几维,写完了记忆化搜索动态转移方程基本就出来了)

3.递归式(转移方程)

4.自底向上计算最优解(编程方式)

如果没思路的话,就先写一遍暴力吧!

附代码

#include<iostream>
#include<cstdio>
using namespace std;
int rf(int m,int n)
{
    int sum=0;
    if(n==1)
        return 1;
    for(int i=1;i<=m;++i)
    {
        if(n-i>=1)
            sum+=rf(m,n-i);
    }
    return sum;
}
int main(void)
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF&&n+m!=0)
    {
       int h= rf(m,n);
       printf("%d\n",h);
    }

}

不用说 ,这里的提交肯定是wa了。

这里和别的博客上说的一样,纯暴力会出现许多重叠子问题。

如图(...感觉让我素描老师看见非得骂我不可)(有什么画图软件推荐码!!)

这里用的4,2得例子,这样就会出现(2,2)算了两次 (冗余)当n,m大了之后非常影响效率,如图

 所以这里我用了记忆化搜索的方法,就是把结果存起来就可以啦(其实蛮符合刚才提到的第二步)

代码如下

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
int dp[35];
int rf(int m,int n)
{
    if(dp[n]>0)
        return dp[n];
    if(n==1)
        return 1;
    for(int i=1;i<=m;++i)
    {
        if(n-i>=1)
            dp[n]+=rf(m,n-i);
    }
    return dp[n];
}
int main(void)
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF&&n+m!=0)
    {
        memset(dp,0,sizeof(dp));
       int h= rf(m,n);
       printf("%d\n",h);
    }

}

这次再提交就过了!

 

然后都到了这一步了...转移方程也是比较明显了..

dp[j]+=dp[j-i](j-i>=1,i<=m)

继续po代码

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
int dp[35];
int main(void)
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF&&n+m!=0)
    {
        memset(dp,0,sizeof(dp));
        dp[1]=1;
        for(int i=2;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                if(i-j>=1)
                    dp[i]+=dp[i-j];
            }
        }
       printf("%d\n",dp[n]);
    }

}

 

好吧..时间上好像没啥优化

这里可能是数据量太小?

emmm初学菜鸡还不是很明白

不过做题思路差不多就是这样啦!

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值