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
#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();
}

你可能感兴趣的:(#,DP与贪心题目,算法)