G - 李白打酒加强版(线性DP)

Posted Turing_Sheep

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了G - 李白打酒加强版(线性DP)相关的知识,希望对你有一定的参考价值。

G - 李白打酒加强版(线性DP)

1、问题

G - 李白打酒加强版

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 i1个花, j j j个店。酒的数量是 k + 1 k+1 k+1
如果当前是店,那么就说明在此之前有 i i i个花, j − 1 j-1 j1个店,酒的数量是 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][j1][k+1]+f[i][j][k])f[i][j][k]=(f[i1][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 m1个花,并且酒的剩余量是 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)的主要内容,如果未能解决你的问题,请参考以下文章

LQ0069 李白打酒加强版DP

C语言递归算法解决李白打酒问题

大战蓝桥杯每日算法详解解析(C/C++)

李白打酒

李白打酒

李白打酒