☆UVALive 7276 Wooden Signs (DP or 记忆化搜索)

42 篇文章 1 订阅


题意:摆木图, 一共n层木头,告诉你第一个木头尾端跟头端的位置,其余n-1层头端的位置,组合方法是,上面图的方法。。。要么尾端跟尾端一起,要么尾端跟头端一起(必须有一定长度的木头支撑,具体看图)

思路:这题一开拿到只知道怎么转移。。3种情况(可以化简为2种),但不知道怎么写啊。。。这木头咋状态表示啊。。后来发现自己真的zz, 一共两头。。而且每个首部都告诉你了,你直接记录尾部的位置就好了啊。。。那样下面木头就固定了,就可以转移了啊。。。真的zz。。dp[i]【j】 表示第i个木头,他的尾部的位置为j,这样他的头部a[i]跟尾部就确定了,那么对于第i个木头,他肯定是从i-1木头转移过来的,确定i-1木头的左右区间,然后就三种转移:一种是i+1的头部在第i根整段的左边,一种是在右边,还有在中间,中间的有两种情况,其他都只有一种

递推代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int Mod = 2147483647;
const int maxn = 2e3 + 5;
ll dp[maxn][maxn];
int a[maxn];
int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        memset(dp, 0, sizeof(dp));
        for(int i = 0; i <= n; i++) scanf("%d", &a[i]);
        dp[1][a[0]] = 1;
        for(int i = 2; i <= n; i++)
        {
            for(int j = 1; j <= n+1; j++)
            {
                int l = min(a[i-1], j), r = max(a[i-1], j);
                if(a[i] <= l)  //三种转移
                {
                    dp[i][r] += dp[i-1][j];
                    dp[i][r] %= Mod;
                }
                else if(a[i] >= r)
                {
                    dp[i][l] += dp[i-1][j];
                    dp[i][l] %= Mod;
                }
                else
                {
                    dp[i][l] += dp[i-1][j];
                    dp[i][r] += dp[i-1][j];
                    dp[i][l] %= Mod;
                    dp[i][r] %= Mod;
                }
            }
        }
        ll ans = 0;
        for(int i = 1; i <= n+1; i++)
        {
            ans += dp[n][i];
            ans %= Mod;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

记忆化代码(把递推的分类简化了一下):

记忆化就不比每种状态都遍历了,从最开始王下遍历,cur代表现在的层数i,ppos代表上一个木头尾部在哪里。。因为转移根据上一个来的

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int Mod = 2147483647;
const int maxn = 2e3 + 5;
ll dp[maxn][maxn];
int a[maxn];
int n;
ll dfs(int cur, int ppos)
{
    if(cur > n) return 1;
    if(dp[cur][ppos]) return dp[cur][ppos];
    int l = min(a[cur-1], ppos), r = max(a[cur-1], ppos);
    if(a[cur] < r) dp[cur][ppos] = (dp[cur][ppos] + dfs(cur+1, r))%Mod;
    if(a[cur] > l) dp[cur][ppos] = (dp[cur][ppos] + dfs(cur+1, l))%Mod;
    return dp[cur][ppos];
}
int main()
{
    while(~scanf("%d", &n))
    {
        for(int i = 0; i <= n; i++)
            scanf("%d", &a[i]);
        memset(dp, 0, sizeof(dp));
        ll ans = dfs(2, a[0]);
        printf("%lld\n", ans);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值