G - 李白打酒加强版(线性DP)
Posted Turing_Sheep
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了G - 李白打酒加强版(线性DP)相关的知识,希望对你有一定的参考价值。
G - 李白打酒加强版(线性DP)
1、问题
2、暴力搜索+剪枝优化:( 50 % 50\\% 50%)
这个方法就很简单了,我们从第一个开始枚举,当前要么是花,要么是店。直接枚举即可,那么如果不加剪枝的话,我们的时间复杂度是: O ( 2 N ) O(2^N) O(2N)。而 N N N的最大值是 100 100 100,所以肯定会超时,为了多过几个数据,我们可以加一些剪枝优化,比如,如果当前的酒已经大于了花的数量,那么肯定是不合法的。
另外,如果当前的酒已经是 0 0 0了,但是剩余未确定的花的数量还大于 0 0 0,那么肯定也是不合法的。
void dfs(ll u, ll drink, ll ren, ll rem)
if(drink > rem)
return;
if(drink == 0 && rem > 0)
return;
if(u > (n + m))
ans = (ans + 1) % mod;
return;
if(u != n + m)
if(ren >= 1)
dfs(u + 1, drink * 2, ren - 1, rem);
if(rem >= 1)
dfs(u + 1, drink - 1, ren, rem - 1);
else
if(rem >= 1)
dfs(u + 1, drink - 1, ren, rem - 1);
3、线性DP:( 100 % 100\\% 100%)
状态表示
f [ n ] [ m ] [ k ] f[n][m][k] f[n][m][k]表示当前有 n n n个店, m m m个花,且我们的酒的量是 k k k的情况下,总共的方案数。
状态转移
在写状态转移方程之前,我们先来确定一下 k k k的上界,为什么呢?
因为当我们遇到店的时候,我们的酒的量会翻倍,如果不断翻倍的话,酒的量会呈现指数型的增长,先不说这种情况合不合理,我们的空间都无法存下。所以我们有必要先去分析一下上界。
由于我们的 N N N的最大值是 100 100 100,所以如果全是花的话,我们的酒量最大值也就是 100 100 100。所以为了保险,我们的上界可以写成 101 101 101。
接下来我们考虑一下状态转移方程怎么写?
假设我们现在的花有
i
i
i个,店有
j
j
j个。酒的数量是
k
k
k。此时,我们可以看前一个是什么,然后写转移方程。
如果当前是花,那么就说明在此之前有
i
−
1
i-1
i−1个花,
j
j
j个店。酒的数量是
k
+
1
k+1
k+1。
如果当前是店,那么就说明在此之前有
i
i
i个花,
j
−
1
j-1
j−1个店,酒的数量是
k
/
2
k/2
k/2,这里的
k
k
k要求能够被
2
2
2整除。
那么方程就可以自然而然地写出来了:
f
[
i
]
[
j
]
[
k
]
=
(
f
[
i
]
[
j
−
1
]
[
k
+
1
]
+
f
[
i
]
[
j
]
[
k
]
)
f
[
i
]
[
j
]
[
k
]
=
(
f
[
i
−
1
]
[
j
]
[
k
/
2
]
+
f
[
i
]
[
j
]
[
k
]
)
f[i][j][k] = (f[i][j - 1][k + 1] + f[i][j][k])\\\\ f[i][j][k] = (f[i - 1][j][k / 2] + f[i][j][k])
f[i][j][k]=(f[i][j−1][k+1]+f[i][j][k])f[i][j][k]=(f[i−1][j][k/2]+f[i][j][k])
初末状态
初始状态很好写:
f
[
0
]
[
0
]
[
2
]
=
1
f[0][0][2]=1
f[0][0][2]=1
末尾状态需要考虑一下,因为最后一个肯定是花,所以我们在最后一个的前面应该有
n
n
n个店,
m
−
1
m-1
m−1个花,并且酒的剩余量是
1
1
1。
代码
#include<bits/stdc++.h>
#define endl '\\n'
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int mod = 1e9 + 7;
const int N = 200 + 10;
ll ans = 0;
ll n, m;
ll f[N][N][N];
void solve()
cin >> n >> m;
f[0][0][2] = 1;
for(int i = 0; i <= n; i ++ )
for(int j = 0; j <= m; j ++ )
for(int k = 0; k <= 101; k ++ )
if(j - 1 >= 0)
f[i][j][k] = (f[i][j - 1][k + 1] + f[i][j][k]) % mod;
if(k % 2 == 0 && i - 1 >= 0)
f[i][j][k] = (f[i - 1][j][k / 2] + f[i][j][k]) % mod;
cout << f[n][m - 1][1] << endl;
int main()
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
李白打酒
李白打酒这个用暴力递归很容易算出来,今天想要加上链路记录,原本以为挺简单的事情,却想了很久才想明白,记录一下吧。
public class LibaiDajiu2 { static char[] c = new char[15]; static int sum = 0; public static void main(String[] args) { System.out.println(fun(5,10,2,0)); } private static int fun(int d , int h, int count, int step) { if( d ==0 && h ==1 && count == 1 ){ sum ++; } if(d> 0){ fun(d-1,h,count*2,step+1); } if(h>0){ fun(d,h-1,count-1,step+1); } return sum; } }
以上是关于G - 李白打酒加强版(线性DP)的主要内容,如果未能解决你的问题,请参考以下文章