动态规划(dp)总结

问题 T: 【动态规划】质数和分解

题目描述
任何大于1的自然数n,都可以写成若干个大于等于2且小于等于n的质数之和的形式(包括只有一个数构成的和表达式的情况),并且可能有不止一种质数和的形式。例如9的质数和表达式就有四种本质不同的形式:9=2+5+2=2+3+2+2=3+3+3=2+7。
这里所谓两个本质相同的表达式,是指可以通过交换其中一个表达式中参加和运算的各个数的位置而直接得到另一个表达式。试编程求解自然数n可以写成多少种本质不同的质数和的表达式。

输入

每一行存放一个自然数n(2≤n≤200)。

输出

输出自然数n的本质不同的质数和表达式的数目。

样例输入 Copy

2
200

样例输出 Copy

1
9845164

思路
完全背包
像这种求当前数可以表示成多少个已知数的和的问题,就使用完全背包

#include <iostream>
#include <cstdio>
using namespace std;
int prime[205],dp[205];
int tot=0;
void getprime()///素数打表
{
   
    for(int i=2;i<=200;i++)
    {
   
        int flag=1;
        for(int j=2;j*j<=i;j++)
            if(i%j==0) flag=0;
        if(flag) prime[tot++]=i;
    }
}
void getans()///完全背包,获取答案
{
   
    getprime();
    dp[0]=1;
    for(int j=0;j<tot;j++)
        for(int i=prime[j];i<=200;i++)
            dp[i]+=dp[i-prime[j]];
}
int main()
{
   
    getans();
    int n;
    while(~scanf("%d",&n))
        printf("%d\n",dp[n]);
}


问题 B: 【动态规划】圣诞树

题目
圣诞特别礼物挂在一棵圣诞树上,这棵树有n层,每层有一件礼物,每件礼物都有一个价值,有的礼物还有一些连接线,与下层的礼物相连。领取礼物的规则如下:任选一件礼物,它的下面如果有连接线,则可以继续取它连接的礼物,依此类推直至取到没有连接线的礼物才结束。你如果是第一个去取,怎样取才能获得最大的价值呢?请你编一程序解决这一问题。
输入
第1行只有一个数据n(n≤100),表示有n层礼物,以下有n行数据,分别表示第1~n层礼物的状态,每行至少由一个数据构成,且第一个数据表示该礼物的价值,后面的数据表示它与哪些层的礼物相连,如果每行只有一个数据则说明这层礼物没有与下层礼物相连,每个数据大小均不超过10000。
输出
只有一个数,表示获得的最大价值。
思路
动态转移方程是:dp[i]=max(a[i],a[i]+dp[j])(j为与i相连的层数)因为礼物是上层连下层,所以从下层开始求最大值,这样可以保证每一次求得的都是最大值,然后记忆化搜索即可
注意
读入比较麻烦,根据每个数后面是空格还是回车判断是否继续输入即可

#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;
int dp[105];
int maxn=0;
vector<int> visit[105];
int a[105];
int slove(int x)
{
   
    if(visit[x].size()==0) return dp[x]=a[x];///没有预其相连的,dp[x]值为a[x],返回
    if(dp[x]) return dp[x];///记忆化,防止重复搜索
    for(int i=0;i<visit[x].size();i++)
    {
   
        int next=visit[x][i];///获取下一个节点
        dp[x]=max(dp[x],slove(next)+a[x]);///dp[x]=max(dp[x],dp[next]+a[x]);
    }
    return dp[x];
}
int main()
{
   
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
   
        scanf("%d",&a[i]);
        char ch=getchar();
        if(ch==' ')
        {
   
            do
            {
   
                int x;
                scanf("%d",&x);
                visit[i].push_back(x);
                ch=getchar();
            }while(ch==' ');
        }
    }
    for(int i=n;i>=1;i--) maxn=max(maxn,slove(i));
    cout<<maxn;
    return 0;
}

问题 C: 传球游戏

题目
上体育课时,墨老师经常带着同学们一起做游戏。这次,墨老师带着同学们一起做传球游戏,游戏规则是这样的:N个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时拿着球没传出去的那个同学就是败者,要给大家表演一个节目。
聪明的张琪曼提出一个有趣的问题:有多少种不同的传球方法可以使得从张琪曼手里开始传的球,传了M次以后,又回到张琪曼手里。两种传球的方法被称作不同的方法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。比如有3个同学1号、2号、3号,并假设张琪曼为1号,球传了3次回到张琪曼手里的方式有1à2à3à1和1à3à2à1,共两种。
输入
有两个用空格隔开的整数N,M(3≤N≤30,1≤M≤30)。
输出
有一个整数,表示符合题目的方法数。
思路
1.第一种方法:使用排列组合求;
1)本题可以转化为求:有m个数相加(这些数只能为1或-1),求有多少种情况使得他们的和为n的倍数(1,-1排的位置不同,则情况不同)
2)那么假设有i个1,j个-1,则abs(i-j)%n==0时,和是符合条件的,然后再将这m个数去排列,可知共有C(m,j)种情况;
3)然后把所有符合条件的情况加起来就是答案了
代码1

#include <iostream>
#include <cmath>
using namespace std;
long long c[35][35];
inline void getC()
{
   
    for(int i=1;i<=30;i++) c[i][0]=c[0][i]=c[i][i]=1;
    for(int i=2;i<=30;i++)
        for(int j=1;j<=30;j++)
            c[i][j]=c[i-1][j-1]+c[i-1][j];
}
int main()
{
   
    getC();
    int n,m;cin>>n>>m;
    long long ans=0;
    for(int i=0;i<=m;i++)
    {
   
        int j=m-i;
        if((int)abs(i-j)%n==0) ans+=c[m][i];
    }
    cout<<ans<<endl;
    return 0;
}

2.使用记忆化搜索
1)dp[i][j]代表传了j次球传到第i个人的情况数,那么我们可以知道dp[i][j]=dp[(i+1)%n][j-1]+dp[(i-1+n)%n][j-1](即传了j-1次传到第i-1个人情况数与传了j-1次传到第i+1个人的情况数的和)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值