Codeforces 182E - Wooden Fence 多阶段决策dp

Codeforces 182E - Wooden Fence 多阶段决策dp

传送门:https://codeforces.com/problemset/problem/182/E

题意

有 n 种 木 板 , 长 为 a i , 宽 为 b i , 当 木 板 旋 转 时 等 于 之 前 , 则 算 一 种 木 板 , 否 则 算 不 同 类 型 。 有n种木板,长为a_i,宽为b_i,当木板旋转时等于之前,则算一种木板,否则算不同类型。 naibi
这 些 木 板 有 无 数 块 , 问 , 能 组 成 多 少 个 长 为 L 的 木 板 。 求 出 方 案 数 。 这些木板有无数块,问,能组成多少个长为L的木板。求出方案数。 L

每 种 木 板 的 长 和 宽 都 能 作 为 “ 长 ” 。 每种木板的长和宽都能作为“长”。

规 则 : 规则:

  • 当 前 选 择 的 木 板 不 能 和 前 一 种 是 同 样 类 型 当前选择的木板不能和前一种是同样类型
  • 当 前 选 择 的 木 板 , 长 要 等 于 上 一 块 的 宽 。 当前选择的木板,长要等于上一块的宽。

思路

先 看 数 据 量 , n = 100 , L = 3000 , D P ! 先看数据量,n=100,L=3000,DP! n=100L=3000DP

如 何 选 取 状 态 ? 首 先 一 定 要 遍 历 每 块 木 板 , 则 一 个 状 态 给 遍 历 到 第 几 块 木 板 。 如何选取状态?首先一定要遍历每块木板,则一个状态给遍历到第几块木板。

如 果 放 下 一 块 木 板 之 后 , 它 的 总 长 会 发 生 变 化 , 而 且 需 要 上 一 个 状 态 的 总 长 。 如果放下一块木板之后,它的总长会发生变化,而且需要上一个状态的总长。
则 当 前 的 总 长 需 要 记 录 , 给 一 个 状 态 给 长 度 。 则当前的总长需要记录,给一个状态给长度。

这 块 木 板 是 横 着 放 , 还 是 竖 着 放 ? 上 一 块 是 横 还 是 竖 ? 而 且 必 须 是 长 等 于 上 一 个 宽 。 这块木板是横着放,还是竖着放?上一块是横还是竖?而且必须是长等于上一个宽。
所 以 需 要 一 个 状 态 记 录 当 前 块 要 怎 么 放 ? 并 且 通 过 上 一 个 木 块 怎 么 放 来 转 移 。 所以需要一个状态记录当前块要怎么放?并且通过上一个木块怎么放来转移。

因 此 , 我 们 得 到 d p [ i ] [ j ] [ 0 / 1 ] , 表 示 放 入 第 j 块 之 后 , 长 度 为 i , 并 且 第 j 快 是 1 ( 宽 放 ) | 0 ( 长 放 ) 。 因此,我们得到dp[i][j][0/1],表示放入第j块之后,长度为i,并且第j快是1(宽放)|0(长放)。 dp[i][j][0/1]jij10

如 何 转 移 ? 如何转移?

需 要 通 过 前 一 块 的 状 态 来 进 行 转 移 : 需要通过前一块的状态来进行转移:

if(a[j] == a[k] && i > a[j] && dp[i - a[j]][k][1]) 
	dp[i][j][0] = (dp[i][j][0] + dp[i - a[j]][k][1]) % mod;
if(a[j] == b[k] && i > a[j] && a[k] != b[k] && dp[i - a[j]][k][0]) 
	dp[i][j][0] = (dp[i][j][0] + dp[i - a[j]][k][0]) % mod;
if(b[j] == b[k] && i > b[j] && dp[i - b[j]][k][0]) 
	dp[i][j][1] = (dp[i][j][1] + dp[i - b[j]][k][0]) % mod;
if(b[j] == a[k] && i > b[j] && a[k] != b[k] && dp[i - b[j]][k][1]) 
	dp[i][j][1] = (dp[i][j][1] + dp[i - b[j]][k][1]) % mod;

这 里 的 a [ k ] ! = b [ k ] , 就 是 避 免 连 续 两 块 类 型 相 同 。 这里的a[k]!=b[k],就是避免连续两块类型相同。 a[k]!=b[k]

看 上 一 块 的 横 放 还 是 竖 放 , 来 转 移 当 前 块 是 横 放 还 是 竖 放 。 看上一块的横放还是竖放,来转移当前块是横放还是竖放。

复 杂 度 为 O ( n ∗ n ∗ L ) 复杂度为O(n*n*L) O(nnL)

Code

#include "bits/stdc++.h"
using namespace std;

typedef long long ll;

// const ll mod = 998244353;
const ll mod = 1e9 + 7;

const int N = 3005;
ll dp[N][N][2]; // dp[i][j][0/1] 长度为i,用到第j个board,长0,宽1

void solve() {
    int n, L; cin >> n >> L;
    vector<int> a(n + 1), b(n + 1);
    for(int i = 1;i <= n; i++) cin >> a[i] >> b[i];
    for(int i = 1;i <= L; i++) {
        for(int j = 1;j <= n; j++) {
            if(a[j] == i) dp[i][j][0]++;
            if(b[j] == i) dp[i][j][1]++;
        }
    }
    for(int i = 1;i <= L; i++) {
        for(int j = 1;j <= n; j++) { // 当前的第j个
            for(int k = 1;k <= n; k++) { // 之前的第k个比较
                if(j == k) continue;
                if(a[j] == a[k] && i > a[j] && dp[i - a[j]][k][1])
                    dp[i][j][0] = (dp[i][j][0] + dp[i - a[j]][k][1]) % mod;
                if(a[j] == b[k] && i > a[j] && a[k] != b[k] && dp[i - a[j]][k][0])
                    dp[i][j][0] = (dp[i][j][0] + dp[i - a[j]][k][0]) % mod;
                if(b[j] == b[k] && i > b[j] && dp[i - b[j]][k][0])
                    dp[i][j][1] = (dp[i][j][1] + dp[i - b[j]][k][0]) % mod;
                if(b[j] == a[k] && i > b[j] && a[k] != b[k] && dp[i - b[j]][k][1])
                    dp[i][j][1] = (dp[i][j][1] + dp[i - b[j]][k][1]) % mod;
        }
    }
    ll ans = 0;
    for(int i = 1;i <= n; i++) {
        ans = (ans + dp[L][i][0]) % mod;
        if(a[i] != b[i]) ans = (ans + dp[L][i][1]) % mod;
    }
    cout << ans << endl;
}

signed main() {
    solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值