传送门:https://codeforces.com/problemset/problem/182/E
题意
有
n
种
木
板
,
长
为
a
i
,
宽
为
b
i
,
当
木
板
旋
转
时
等
于
之
前
,
则
算
一
种
木
板
,
否
则
算
不
同
类
型
。
有n种木板,长为a_i,宽为b_i,当木板旋转时等于之前,则算一种木板,否则算不同类型。
有n种木板,长为ai,宽为bi,当木板旋转时等于之前,则算一种木板,否则算不同类型。
这
些
木
板
有
无
数
块
,
问
,
能
组
成
多
少
个
长
为
L
的
木
板
。
求
出
方
案
数
。
这些木板有无数块,问,能组成多少个长为L的木板。求出方案数。
这些木板有无数块,问,能组成多少个长为L的木板。求出方案数。
每 种 木 板 的 长 和 宽 都 能 作 为 “ 长 ” 。 每种木板的长和宽都能作为“长”。 每种木板的长和宽都能作为“长”。
规 则 : 规则: 规则:
- 当 前 选 择 的 木 板 不 能 和 前 一 种 是 同 样 类 型 当前选择的木板不能和前一种是同样类型 当前选择的木板不能和前一种是同样类型
- 当 前 选 择 的 木 板 , 长 要 等 于 上 一 块 的 宽 。 当前选择的木板,长要等于上一块的宽。 当前选择的木板,长要等于上一块的宽。
思路
先 看 数 据 量 , n = 100 , L = 3000 , D P ! 先看数据量,n=100,L=3000,DP! 先看数据量,n=100,L=3000,DP!
如 何 选 取 状 态 ? 首 先 一 定 要 遍 历 每 块 木 板 , 则 一 个 状 态 给 遍 历 到 第 几 块 木 板 。 如何选取状态?首先一定要遍历每块木板,则一个状态给遍历到第几块木板。 如何选取状态?首先一定要遍历每块木板,则一个状态给遍历到第几块木板。
如
果
放
下
一
块
木
板
之
后
,
它
的
总
长
会
发
生
变
化
,
而
且
需
要
上
一
个
状
态
的
总
长
。
如果放下一块木板之后,它的总长会发生变化,而且需要上一个状态的总长。
如果放下一块木板之后,它的总长会发生变化,而且需要上一个状态的总长。
则
当
前
的
总
长
需
要
记
录
,
给
一
个
状
态
给
长
度
。
则当前的总长需要记录,给一个状态给长度。
则当前的总长需要记录,给一个状态给长度。
这
块
木
板
是
横
着
放
,
还
是
竖
着
放
?
上
一
块
是
横
还
是
竖
?
而
且
必
须
是
长
等
于
上
一
个
宽
。
这块木板是横着放,还是竖着放?上一块是横还是竖?而且必须是长等于上一个宽。
这块木板是横着放,还是竖着放?上一块是横还是竖?而且必须是长等于上一个宽。
所
以
需
要
一
个
状
态
记
录
当
前
块
要
怎
么
放
?
并
且
通
过
上
一
个
木
块
怎
么
放
来
转
移
。
所以需要一个状态记录当前块要怎么放?并且通过上一个木块怎么放来转移。
所以需要一个状态记录当前块要怎么放?并且通过上一个木块怎么放来转移。
因 此 , 我 们 得 到 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],表示放入第j块之后,长度为i,并且第j快是1(宽放)|0(长放)。
如 何 转 移 ? 如何转移? 如何转移?
需 要 通 过 前 一 块 的 状 态 来 进 行 转 移 : 需要通过前一块的状态来进行转移: 需要通过前一块的状态来进行转移:
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(n∗n∗L)
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();
}