文章目录
1.第一题(迷宫权值)
数据规模和约定
对于30%的评测用例,1 <= n, m <= 10;
对于50%的评测用例,1 <= n, m <= 20;
对于所有评测用例,1 <= n <= 100,-10000 <= 权值 <= 10000。
输出格式
输出一个整数,表示最大权值和。
样例输入
3 5
-4 -5 -10 -3 1
7 5 -9 3 -10
10 -2 6 -10 -4
样例输出:15
思路:dp问题,反转思路,对于一个点的权值 = 其左/上方能到达的点的最大权值 + 自己的权值,依次求出每个点最大的权值即可
/* 状态转移方程
x = 1 且 y = 1 f = a[x][y]
other f = a[x][y] + max{w[x][y-123]、w[x-1][y-012]、w[x-2][y-01]、w[x-3][y]}
减123的意思是y-1、y-2、y-3
*/
#include<iostream>
#include<algorithm>
using namespace std;
int a[101][101], w[101][101], r, c;
const int INF = 0xc0c0c0c0; // 负无穷
int dp(int x, int y){
if (x == 1 && y == 1) return a[1][1];
int m = INF;
// max{w[x][y-123]}
for (int i = 1; i <= 3; i++){
if ((y - i) > 0) m = max(m, w[x][y - i]);
}
// max{w[x-1][y-012]}
for (int i = 0; i <= 2; i++){
if ((y - i) > 0 && (x - 1) > 0) m = max(m, w[x - 1][y - i]);
}
// max{w[x-2][y-01]}
for (int i = 0; i <= 1; i++){
if ((y - i) > 0 && (x - 2) > 0) m = max(m, w[x - 2][y - i]);
}
// max{m, w[x-3][y]}
if ((x - 3) > 0) m = max(m, w[x - 3][y]);
return a[x][y] + m;
}
int main(){
cin >> r >> c;
for (int i = 1; i <= r; i++){
for (int j = 1; j <= c; j++){
cin >> a[i][j];
}
}
for (int i = 1; i <= r; i++){
for (int j = 1; j <= c; j++){
w[i][j] = dp(i, j);
}
}
/* 输出每个位置的最大权重
for (int i = 1; i <= r; i++){
for (int j = 1; j <= c; j++){
cout << w[i][j] << " ";
}
cout << endl;
}
*/
cout << w[r][c] << endl;
return 0;
}
当时没想到dp,想到的是dfs,示例是对的,但应该比较耗时,代码如下
#include<iostream>
#include<stack>
#include<cmath>
using namespace std;
int n, m;
int maze[102][102] = {0};
int dic[9][2]={{0,1},{0,2},{0,3},{1,0},{1,1},{1,2},{2,0},{2,1},{3,0}};
long long ans;
struct Point{
int x, y;
long long value;
Point(int a, int b, long long c):x(a), y(b), value(c){}
};
stack<Point> st;
bool in(int i, int j){
return (i >= 1 && j >= 1 && i <= n && j <= m);
}
void dfs(){
while(!st.empty()){
Point tp = st.top();
st.pop();
if(tp.x == n && tp.y == m){
ans = max(ans, tp.value);
continue;
}
for(int i = 0; i < 9; i++){
int dx = tp.x + dic[i][0];
int dy = tp.y + dic[i][1];
if(in(dx, dy)){
Point a(dx, dy, tp.value + maze[dx][dy]);
st.push(a);
}
}
}
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cin >> maze[i][j];
}
}
Point p(1,1,maze[1][1]);
st.push(p);
dfs();
cout << ans;
return 0;
}
参考:https://blog.csdn.net/weixin_43521592/article/details/112489745
2.第二题
第十一届蓝桥杯C++
画中漂流
【问题描述】
在梦境中,你踏上了一只木筏,在江上漂流。根据对当地的了解,你知道在你下游 D 米处有一个峡谷,如果你向下游前
进大于等于 D 米则必死无疑。现在你打响了急救电话,T 秒后救援队会到达并将你救上岸。水流速度是1 m/s,你现在有 M 点体力。每消耗一点体力,你可以划一秒桨使船向上游前进 1m,否则会向下游前进 1m (水流)。M 点体力需在救援队赶来前花光。因为江面太宽了,凭借你自己的力量不可能上岸。请问,有多少种划桨的方案可以让你得救。
两个划桨方案不同是指:存在某一秒钟,一个方案划桨,另一个方案不划。
【输入格式】
输入一行包含三个整数 D, T, M。
【输出格式】
输出一个整数,表示可以让你得救的总方案数,答案可能很大,请输出方
案数除以 1, 000, 000, 007 的余数
【样例输入】
1 6 3
【样例输出】
5
【评测用例规模与约定】
对于 50% 的评测用例,1 ≤ T ≤ 350。
对于所有评测用例,1 ≤ T ≤ 3000, 1 ≤ D ≤ T, 1 ≤ M ≤ 1500。
思路
我们可以发现,每i秒之间的状态是固定的(在哪个可达位置)所以这些可达状态必然是从上一层的合法方案的i+1秒或i-1秒到达的所以可以用一个dp.
f[i][j] 表示在第i秒在位置k的方案数
f[i][j] = f[i - 1][j - 1] + f[i - 1][j + 1]
每层计算需要卡i的区间即可
代码
#include<iostream>
using namespace std;
const int N = 3010,M = 60010,K = 3000,mod = 1e9+7;
int f[N][M];
int main(){
int D,n,m;
scanf("%d%d%d",&D,&n,&m);
f[0][K] = 1;
for(int i = 1;i <= n;i++){
// 可以看做每分钟下游一次,如果上划则滑2次
// 向上游最多m次 当前最多i次 下滑最多i次且至少要保证大于D
for(int j = K + i - min(2 * i , 2 * m);j <=min(K + i,K + D - 1);j++){
f[i][j] = (0ll + f[i - 1][j - 1] + f[i - 1][j + 1])%mod;
}
}
long long ans = 0;
// 统计所有的可能
for(int j = K + n - min(2 * n,2 * m);j <=min(K + n,K + D - 1);j++){
ans = (ans + f[n][j])% mod;
}
cout << ans <<endl;
return 0;
}
3.
样例输入
6
1 3 2 3 4 5
1
2
样例输出
6
1
数据规模和约定
对于40%的数据,n<=50;
对于100%的数据,n<=2000,0<=wi<=109。
思路:
找递推公式:
dp[i][k]表示以第i个数为结尾的,递增k个数的序列个数
最外层k是控制问题规模的(k从2到4)(先求小规模问题,再求大规模问题)
所以找dp[i][k]
当a[i]>a[j]时,dp[i][k]+=dp[j][k-1];
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll dp[2001][5];//dp[i][j]表示以i为结尾的递增j个数的序列个数
ll a[2001];
int main()
{
memset(dp,0,sizeof dp);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++){
dp[i][1]=1;
}
for(int k=2;k<=4;k++){//k表示连续k个数递增
for(int i=2;i<=n;i++){//i为终止边界
for(int j=1;j<i;j++){//j为从最左到最右边找
if(a[i]>a[j]){
dp[i][k]+=dp[j][k-1];
}
}
}
}
ll ans=0;
for(int i=4;i<=n;i++){
ans+=dp[i][4];
}
printf("%lld",ans);
//注意输出以每个为结尾连续递增4个数的和,而不是只输出dp[n][4]
return 0;
}
参考:https://blog.csdn.net/timelessx_x/article/details/115568745