记忆化搜索

三倍经验

题目描述

数字金字塔由 n n n 行整数组成,第 i ( 1 ≤ i ≤ n ) i(1\le i\le n) i(1in) 行有 i i i 个数字,一个示例如下。

        7
      3   9
    8   1   0
  2   7   4   4 
4   5   2   6   5

现在你在金字塔的顶部(第一行),你希望走到金字塔的底部(第 n n n 行),每一步你只能走向当前所在位置的左下方的数字或者右下方的数字。同时作为一个强大的小朋友,你可以选择金字塔中的不多于 k k k 个数字让他们成为原来的 3 3 3 倍。

你会收集你路上经过的所有位置上的数字,最后的得分即为收集的数字之和,求最大得分。

输入格式

第一行输入两个整数 n , k n,k n,k,表示数字金字塔的行数和乘 3 3 3 的数字个数最大值;
接下来 n n n 行,其中的第 i i i 行有 i i i 个以空格隔开的整数依次表示数字金字塔第 i i i 行的数字 a i , 1 , a i , 2 , a i , 3 . . . a i , i a_{i,1},a_{i,2},a_{i,3}...a_{i,i} ai,1,ai,2,ai,3...ai,i

输出格式

一行一个整数,表示最大得分。

样例 #1

样例输入 #1

5 3
7
3 9
8 1 0
2 7 4 4
4 5 2 6 5

样例输出 #1

75

提示

对于 30 % 30\% 30% 的数据,满足 k ≤ n ≤ 6 k\le n\le 6 kn6,并且对于任意 1 ≤ i ≤ n 1\le i\le n 1in 1 ≤ j ≤ i 1\le j\le i 1ji 满足 0 ≤ a i , j ≤ 100 0\le a_{i,j}\le 100 0ai,j100
对于 100 % 100\% 100% 的数据,满足 1 ≤ n ≤ 100 1\le n\le100 1n100 0 ≤ k ≤ n ( n + 1 ) 2 0\le k\le \dfrac{n(n+1)}{2} 0k2n(n+1),且对于任意 1 ≤ i ≤ n 1\le i\le n 1in 1 ≤ j ≤ i 1\le j\le i 1ji 满足 ∣ a i , j ∣ ≤ 1 0 9 |a_{i,j}|\le 10^9 ai,j109
搜索过程:
根据有没有乘三倍的机会,分开搜索,然后再细分左边还是右边
1.越界直接return 0;
2.搜过当前状态,直接return
3.搜索
4.返回答案

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#define int long long
using namespace std;
int n, m;
int w[105][105];
int f[105][105][105];
bool st[150][150][150];
int dfs(int x, int y, int num)
{
	if (x <= 0 || y <= 0 || x > n || y > x)
	{
		return 0;
	}
	if (st[x][y][num]) return f[x][y][num];
	if (num) {
		f[x][y][num] = max(f[x][y][num], dfs(x + 1, y, num - 1) + 3 * w[x][y]);
		f[x][y][num] = max(f[x][y][num], dfs(x + 1, y + 1, num - 1) + 3 * w[x][y]);
	}
	f[x][y][num] = max(f[x][y][num], dfs(x + 1, y, num) + w[x][y]);
	f[x][y][num] = max(f[x][y][num], dfs(x + 1, y + 1, num) + w[x][y]);
	st[x][y][num] = 1;
	return f[x][y][num];
}
signed main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= i; j++)
			cin >> w[i][j];
	}
	memset(f, -0x7f, sizeof f);
	cout << dfs(1, 1, m) << endl;
	return 0;
}

[SHOI2002] 滑雪

题目描述

Michael 喜欢滑雪。这并不奇怪,因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael 想知道在一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子:

1   2   3   4   5
16  17  18  19  6
15  24  25  20  7
14  23  22  21  8
13  12  11  10  9

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度会减小。在上面的例子中,一条可行的滑坡为 24 − 17 − 16 − 1 24-17-16-1 2417161(从 24 24 24 开始,在 1 1 1 结束)。当然 25 25 25 24 24 24 23 23 23 … \ldots 3 3 3 2 2 2 1 1 1 更长。事实上,这是最长的一条。

输入格式

输入的第一行为表示区域的二维数组的行数 R R R 和列数 C C C。下面是 R R R 行,每行有 C C C 个数,代表高度(两个数字之间用 1 1 1 个空格间隔)。

输出格式

输出区域中最长滑坡的长度。

样例 #1

样例输入 #1

5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

样例输出 #1

25

提示

对于 100 % 100\% 100% 的数据, 1 ≤ R , C ≤ 100 1\leq R,C\leq 100 1R,C100

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;
int t, m,n;
int g[105][105];
int f[105][102];
int dx[] = { 1,0,-1,0 };
int dy[] = { 0,-1,0,1 };
int dp(int x, int y)
{
	if (f[x][y] != -1) return f[x][y];
	f[x][y] = 1;//保底有一个
	for (int i = 0; i < 4; i++)
	{
		int xx = x + dx[i], yy = y + dy[i];
		if (xx >= 1 &&xx <= n && yy >= 1 && yy <= m && g[x][y] > g[xx][yy])
			f[x][y] = max(f[x][y], dp(xx, yy) + 1);
	}
	return f[x][y];
}
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
			cin >> g[i][j];
	}
	memset(f, -1, sizeof f);
	int res = 0;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
			res = max(res, dp(i, j));
	}
	cout << res << endl;
	return 0;
}

[NOIP2012 普及组] 摆花

题目描述

小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共 m m m 盆。通过调查顾客的喜好,小明列出了顾客最喜欢的 n n n 种花,从 1 1 1 n n n 标号。为了在门口展出更多种花,规定第 i i i 种花不能超过 a i a_i ai 盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。

试编程计算,一共有多少种不同的摆花方案。

输入格式

第一行包含两个正整数 n n n m m m,中间用一个空格隔开。

第二行有 n n n 个整数,每两个整数之间用一个空格隔开,依次表示 a 1 , a 2 , ⋯   , a n a_1,a_2, \cdots ,a_n a1,a2,,an

输出格式

一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对 1 0 6 + 7 10^6+7 106+7 取模的结果。

样例 #1

样例输入 #1

2 4
3 2

样例输出 #1

2

提示

【数据范围】

对于 20 % 20\% 20% 数据,有 0 < n ≤ 8 , 0 < m ≤ 8 , 0 ≤ a i ≤ 8 0<n \le 8,0<m \le 8,0 \le a_i \le 8 0<n8,0<m8,0ai8

对于 50 % 50\% 50% 数据,有 0 < n ≤ 20 , 0 < m ≤ 20 , 0 ≤ a i ≤ 20 0<n \le 20,0<m \le 20,0 \le a_i \le 20 0<n20,0<m20,0ai20

对于 100 % 100\% 100% 数据,有 0 < n ≤ 100 , 0 < m ≤ 100 , 0 ≤ a i ≤ 100 0<n \le 100,0<m \le 100,0 \le a_i \le 100 0<n100,0<m100,0ai100

NOIP 2012 普及组 第三题

#include<bits/stdc++.h>
#define int long long
using namespace std;
int w[105];
int f[105][155];
int ans;
int n,m;
int dfs(int x,int num)
{
	int ans=0;//每次归零表示在当前状态下的方案数
	if(num==m) return 1;//有方案就放前面
	if(x>n||num>m) return 0;//一定是排除
	if(f[x][num]) return f[x][num];//在记忆化
	for(int i=0;i<=w[x];i++){
		ans=(ans+dfs(x+1,num+i))%1000007;
	}
	f[x][num]=ans;//状态表示
	return ans;//返回答案
}
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>w[i];
	cout<<dfs(1,0)%1000007<<endl;
	
} 

没有上司的舞会

题目描述

某大学有 n n n 个职员,编号为 1 … n 1\ldots n 1n

他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。

现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数 r i r_i ri,但是呢,如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。

所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。

输入格式

输入的第一行是一个整数 n n n

2 2 2 到第 ( n + 1 ) (n + 1) (n+1) 行,每行一个整数,第 ( i + 1 ) (i+1) (i+1) 行的整数表示 i i i 号职员的快乐指数 r i r_i ri

( n + 2 ) (n + 2) (n+2) 到第 2 n 2n 2n 行,每行输入一对整数 l , k l, k l,k,代表 k k k l l l 的直接上司。

输出格式

输出一行一个整数代表最大的快乐指数。

样例 #1

样例输入 #1

7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5

样例输出 #1

5

提示

数据规模与约定

对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 6 × 1 0 3 1\leq n \leq 6 \times 10^3 1n6×103 − 128 ≤ r i ≤ 127 -128 \leq r_i\leq 127 128ri127 1 ≤ l , k ≤ n 1 \leq l, k \leq n 1l,kn,且给出的关系一定是一棵树。

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
int w[6005];
int n;
bool vis[6005];
vector<int>ans[6005];
int f[6005][5];
int dfs(int x,bool go){
	if (!ans[x].size()) return w[x] * go;
	if (f[x][go]) return f[x][go];
   
	int res = 0;
	if (go)
	{
		for (int i = 0; i < ans[x].size(); i++)
		{
			res += dfs(ans[x][i], 0);
		}
	}
	else
	{
		for (int i = 0; i < ans[x].size(); i++)
		{
			res += max(dfs(ans[x][i], 1),dfs(ans[x][i],0));
		}
	}
	if (go) return f[x][go]=res + w[x];
	else return f[x][go]=res;//边return边记忆化
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> w[i];
	for (int i = 1; i <= n - 1; i++)
	{
		int a, b;
		cin >> a >> b;
		ans[b].push_back(a);
		vis[a] = 1;

	}
	for (int i = 1; i <= n; i++)
	{
		if (!vis[i])
		cout << max(dfs(i, 1), dfs(i, 0));//从头搜
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值