当前位置: 首页 > article >正文

洛谷P1004方格取数(两个题解)P1359租用游艇 P2285打鼹鼠 P1725琪露诺 P1886滑动窗口/单调序列

P1004 [NOIP 2000 提高组] 方格取数

题目背景

NOIP 2000 提高组 T4

题目描述

设有 N × N N \times N N×N 的方格图 ( N ≤ 9 ) (N \le 9) (N9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 0 0 0。如下图所示(见样例):

某人从图的左上角的 A A A 点出发,可以向下行走,也可以向右走,直到到达右下角的 B B B 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 0 0 0)。
此人从 A A A 点到 B B B 点共走两次,试找出 2 2 2 条这样的路径,使得取得的数之和为最大。

输入格式

输入的第一行为一个整数 N N N(表示 N × N N \times N N×N 的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的 0 0 0 表示输入结束。

输出格式

只需输出一个整数,表示 2 2 2 条路径上取得的最大的和。

输入输出样例 #1

输入 #1

8
2 3 13
2 6  6
3 5  7
4 4 14
5 2 21
5 6  4
6 3 15
7 2 14
0 0  0

输出 #1

67

说明/提示

数据范围: 1 ≤ N ≤ 9 1\le N\le 9 1N9

#include<bits/stdc++.h>
using namespace std;
int x,y,z,n,a[12][12],f[12][12][12][12];
int main(){
	cin >> n >> x >> y >> z;
	while(x!=0||y!=0||z!=0) {
		a[x][y] = z;
		cin >> x >> y >> z;
	}
	for(int i = 1; i <= n; i++)
	for(int j = 1; j <= n; j++)
	for(int k = 1; k <= n; k++)
	for(int l = 1; l <= n; l++){
	f[i][j][k][l] = max(max(f[i-1][j][k-1][l],f[i-1][j][k][l-1]),max(f[i][j-1][k-1][l],f[i][j-1][k][l-1]))+a[i][j]+a[k][l];
    if(i==k&&j==l)f[i][j][k][l]-=a[i][l];
    }
	cout << f[n][n][n][n];
	return 0;
}

附上次的题解
某人从图的左上角的 A A A 点出发,可以向下行走,也可以向右走,直到到达右下角的 B B B 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 0 0 0)。
此人从 A A A 点到 B B B 点共走两次,试找出 2 2 2 条这样的路径,使得取得的数之和为最大。
题解:深搜,因为要两条路都而且是两条路同时进行,每次经过一个点,相加在改值为零,自己写的时候时间复杂度太高而且遍历没有考虑重复,看题解区有所剪枝,注意在(n,n)点回去的时候要减去其值,可以当成一条路,从A到B再回到A
第一条路f=0,第二条路f=1

#include<bits/stdc++.h>
using namespace std;

const int dx[]={0, 1, 0, -1},dy[]={1, 0, -1, 0};

int map[10][10], n; 
int max1=0,ans=0;
inline void dfs(int x, int y, int z, int f) 
{
	if (x<1||y<1||x>n||y>n) return; 
	
	if (x==n&&y==n) 
	{
		f=1;
		z-=map[n][n];
		
		if (z>max1) 
			max1=max(max1, z);
		else
			return;
	}
		
	if (x==1&&y==1&&f==1) //
		ans=max(ans, z);
	
	if (!f) 
	{
		for (int i=0; i<2; i++)
		{
			int lx=x+dx[i],ly=y+dy[i],t=map[lx][ly];
			
			map[lx][ly]=0;
			dfs(lx, ly, z+t, f);
			map[lx][ly]=t;
		}
	}
	else //第二条路
	{
		for (int i=2; i<4; i++)
		{
			int rx=x+dx[i], ry=y+dy[i], t=map[rx][ry];
			map[rx][ry]=0;
			dfs(rx, ry, z+t, f);
			map[rx][ry]=t;
		}
	}
}

int main()
{
	cin>>n;
	
	while(1)
	{
		int x, y, z;
		cin>>x>>y>>z;
		
		if (x==0 && y==0 && z==0)
			break;
		
		map[x][y]=z;
	}
	dfs(1, 1, 0, 0);
	cout<<ans<<endl;
	return 0;
}

P1359 租用游艇

题目描述

长江游艇俱乐部在长江上设置了 n n n 个游艇出租站 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n。游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站 i i i 到游艇出租站 j j j 之间的租金为 r ( i , j ) r(i,j) r(i,j) 1 ≤ i < j ≤ n 1\le i\lt j\le n 1i<jn)。试设计一个算法,计算出从游艇出租站 1 1 1 到游艇出租站 n n n 所需的最少租金。

输入格式

第一行中有一个正整数 n n n,表示有 n n n 个游艇出租站。接下来的 n − 1 n-1 n1 行是一个半矩阵 r ( i , j ) r(i,j) r(i,j) 1 ≤ i < j ≤ n 1\le i<j\le n 1i<jn)。

输出格式

输出计算出的从游艇出租站 1 1 1 到游艇出租站 n n n 所需的最少租金。

输入输出样例 #1

输入 #1

3
5 15
7

输出 #1

12

说明/提示

n ≤ 200 n\le 200 n200,保证计算过程中任何时刻数值都不超过 1 0 6 10^6 106

Floyd算法,图只有上三角,事实上有没有向都是一样的,因为循环中 j = i + 1并不会考虑下三角

#include <bits/stdc++.h>
using namespace std;
#define N 1010
int n, a[N][N], dp[N];

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n; j++) {
            cin >> a[i][j];
        }
    }
    for (int i = 1; i <= n; i++) {
        if (i == 1)
            dp[i] = 0; 
        else
            dp[i] = a[1][i]; 
    }
    for (int k = 2; k <= n; k++) { 
        for (int i = 1; i <= n; i++) { 
            for (int j = i+1; j <= n; j++) { 
                if (i != j && a[i][j] != 0) { 
                    dp[j] = min(dp[j], dp[i] + a[i][j]); 
                }
            }
        }
    }

    cout << dp[n] ;
    return 0;
}

P2285 [HNOI2004] 打鼹鼠

题目描述

鼹鼠是一种很喜欢挖洞的动物,但每过一定的时间,它还是喜欢把头探出到地面上来透透气的。根据这个特点阿牛编写了一个打鼹鼠的游戏:在一个 n × n n \times n n×n 的网格中,在某些时刻鼹鼠会在某一个网格探出头来透透气。你可以控制一个机器人来打鼹鼠,如果 i i i 时刻鼹鼠在某个网格中出现,而机器人也处于同一网格的话,那么这个鼹鼠就会被机器人打死。而机器人每一时刻只能够移动一格或停留在原地不动。机器人的移动是指从当前所处的网格移向相邻的网格,即从坐标为 ( i , j ) (i, j) (i,j) 的网格移向 ( i − 1 , j ) , ( i + 1 , j ) , ( i , j − 1 ) , ( i , j + 1 ) (i-1, j), (i+1, j), (i, j-1), (i, j+1) (i1,j),(i+1,j),(i,j1),(i,j+1) 四个网格,机器人不能走出整个 n × n n \times n n×n 的网格。游戏开始时,你可以自由选定机器人的初始位置。

现在知道在一段时间内,鼹鼠出现的时间和地点,请编写一个程序使机器人在这一段时间内打死尽可能多的鼹鼠。

输入格式

第一行为 n , m n, m n,m n ≤ 1000 n \le 1000 n1000 m ≤ 10 4 m \le {10}^4 m104),其中 m m m 表示在这一段时间内出现的鼹鼠的个数,接下来的 m m m 行中每行有三个数据 t i m e , x , y \mathit{time}, x, y time,x,y 表示在游戏开始后 t i m e \mathit{time} time 个时刻,在第 x x x 行第 y y y 个网格里出现了一只鼹鼠。 t i m e \mathit{time} time 按递增的顺序给出。注意同一时刻可能出现多只鼹鼠,但同一时刻同一地点只可能出现一只鼹鼠。

输出格式

仅包含一个正整数,表示被打死鼹鼠的最大数目。

输入输出样例 #1

输入 #1

2 2	         
1 1 1		
2 2 2

输出 #1

1
#include <iostream>
#include <cmath>
#include <cstdio>

using namespace std;

struct node
{
	int t,x,y;
}mou[10005];

int n,m,ans=-1e9,dp[10005];

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&mou[i].t,&mou[i].x,&mou[i].y);
	}
	for(int i=1;i<=m;i++)
	{
		dp[i]=1; //以第i只鼹鼠结尾的打鼹鼠序列 
		for(int j=1;j<i;j++) //枚举前面的鼹鼠 
		{
			int ddx=abs(mou[i].x-mou[j].x);
			int ddy=abs(mou[i].y-mou[j].y);
			int len=ddx+ddy;//曼哈顿距离 
			int time=mou[i].t-mou[j].t;
			if(time>=len)
			{
				dp[i]=max(dp[i],dp[j]+1);
			}
		}
		ans=max(ans,dp[i]); 
	}
	printf("%d\n",ans); 
	return 0;
}

P1725 琪露诺

题目描述

在幻想乡,琪露诺是以笨蛋闻名的冰之妖精。

某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来。但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸。于是琪露诺决定到河岸去追青蛙。

小河可以看作一列格子依次编号为 0 0 0 N N N,琪露诺只能从编号小的格子移动到编号大的格子。而且琪露诺按照一种特殊的方式进行移动,当她在格子 i i i 时,她只移动到区间 [ i + L , i + R ] [i+L,i+R] [i+L,i+R] 中的任意一格。你问为什么她这么移动,这还不简单,因为她是笨蛋啊。

每一个格子都有一个冰冻指数 A i A_i Ai,编号为 0 0 0 的格子冰冻指数为 0 0 0。当琪露诺停留在那一格时就可以得到那一格的冰冻指数 A i A_i Ai。琪露诺希望能够在到达对岸时,获取最大的冰冻指数,这样她才能狠狠地教训那只青蛙。

但是由于她实在是太笨了,所以她决定拜托你帮它决定怎样前进。

开始时,琪露诺在编号 0 0 0 的格子上,只要她下一步的位置编号大于 N N N 就算到达对岸。

输入格式

第一行三个正整数 N , L , R N, L, R N,L,R

第二行共 N + 1 N+1 N+1 个整数,第 i i i 个数表示编号为 i − 1 i-1 i1 的格子的冰冻指数 A i − 1 A_{i-1} Ai1

输出格式

一个整数,表示最大冰冻指数。

输入输出样例 #1

输入 #1

5 2 3
0 12 3 11 7 -2

输出 #1

11

说明/提示

对于 60 % 60\% 60% 的数据, N ≤ 1 0 4 N \le 10^4 N104

对于 100 % 100\% 100% 的数据, N ≤ 2 × 1 0 5 N \le 2\times 10^5 N2×105,数据保证最终答案不超过 2 31 − 1 2^{31}-1 2311

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,l,r,a[400005];
    deque<int>q;
	cin >> n >> l >> r;
	for(int i = 0; i <= n; i++)cin >> a[i];
    q.push_back(n+1);//把末尾之后的0放进去
	for(int i = n - l ;i >= 0; i--){//必须从后面开始,然后迭代回去,对于每个i都只需要考虑i+l到i+r的窗口,我们从n-l开始
        if(q.front() > i+r)q.pop_front();//如果队首大于窗口,那么弹出
        if(!q.empty()){
            while(a[i+l]>a[q.back()]){//新进来的元素大于原来窗口的最大值的话
                q.pop_back();//就把最大值弹出
                if(q.empty())break;//一直弹出到空就退出
            }
        }
        q.push_back(i+l);//那么如果队列空就直接加
		a[i] += a[q.front()];//因此a[i]只需要加上其对应窗口的最大值即a[q.front()]就好了
	}
	cout << a[0] ;
	return 0;
}

这道题有涉及到单调队列,再来看看洛谷P1886模板单调序列

P1886 滑动窗口 /【模板】单调队列

题目描述

有一个长为 n n n 的序列 a a a,以及一个大小为 k k k 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。

例如,对于序列 [ 1 , 3 , − 1 , − 3 , 5 , 3 , 6 , 7 ] [1,3,-1,-3,5,3,6,7] [1,3,1,3,5,3,6,7] 以及 k = 3 k = 3 k=3,有如下过程:

窗口位置 最小值 最大值 [1   3  -1] -3   5   3   6   7  − 1 3  1  [3  -1  -3]  5   3   6   7  − 3 3  1   3 [-1  -3   5]  3   6   7  − 3 5  1   3  -1 [-3   5   3]  6   7  − 3 5  1   3  -1  -3  [5   3   6]  7  3 6  1   3  -1  -3   5  [3   6   7] 3 7 \def\arraystretch{1.2} \begin{array}{|c|c|c|}\hline \textsf{窗口位置} & \textsf{最小值} & \textsf{最大值} \\ \hline \verb![1 3 -1] -3 5 3 6 7 ! & -1 & 3 \\ \hline \verb! 1 [3 -1 -3] 5 3 6 7 ! & -3 & 3 \\ \hline \verb! 1 3 [-1 -3 5] 3 6 7 ! & -3 & 5 \\ \hline \verb! 1 3 -1 [-3 5 3] 6 7 ! & -3 & 5 \\ \hline \verb! 1 3 -1 -3 [5 3 6] 7 ! & 3 & 6 \\ \hline \verb! 1 3 -1 -3 5 [3 6 7]! & 3 & 7 \\ \hline \end{array} 窗口位置[1   3  -1] -3   5   3   6   7  1  [3  -1  -3]  5   3   6   7  1   3 [-1  -3   5]  3   6   7  1   3  -1 [-3   5   3]  6   7  1   3  -1  -3  [5   3   6]  7  1   3  -1  -3   5  [3   6   7]最小值133333最大值335567

输入格式

输入一共有两行,第一行有两个正整数 n , k n,k n,k
第二行 n n n 个整数,表示序列 a a a

输出格式

输出共两行,第一行为每次窗口滑动的最小值
第二行为每次窗口滑动的最大值

输入输出样例 #1

输入 #1

8 3
1 3 -1 -3 5 3 6 7

输出 #1

-1 -3 -3 -3 3 3
3 3 5 5 6 7

说明/提示

【数据范围】
对于 50 % 50\% 50% 的数据, 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105
对于 100 % 100\% 100% 的数据, 1 ≤ k ≤ n ≤ 1 0 6 1\le k \le n \le 10^6 1kn106 a i ∈ [ − 2 31 , 2 31 ) a_i \in [-2^{31},2^{31}) ai[231,231)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e7+10;
int a[N] , q[N];
int main(){
	int n , k ;
	cin >> n >> k;
	for(int i = 1; i <= n ; i++)cin >> a[i];
	
	//维护窗口内的升序子序列 (维护最小值)
	int h = 1, t = 0;//在窗口中h在左,t在右
	for(int i = 1; i<=n;i++){
		while(h <= t && a[q[t]] >= a[i])t--;//新元素更小那么队尾出队
		q[++t] = i;			//队尾入队(存储队尾的下标,方便判断队头出队)
		if(q[h] < i - k + 1) h++;//队头出队(队头(队头的下标)的元素滑出窗口) 
		if(i >= k)cout << a[q[h]] << " "; 
    }

       cout << endl;
	//维护窗口内的降序子序列(维护最大值)
		h = 1,t = 0;
		for(int i = 1; i<=n; i++){
		while(h <= t && a[q[t]] <= a[i])t--;
		q[++t] = i;
		if(q[h] < i - k + 1) h++;
		if(i >= k)cout << a[q[h]] << " ";
	}

}

转载


http://www.kler.cn/a/568522.html

相关文章:

  • 【沙漠之心:揭秘尘封奇迹的终极之旅】
  • Redis通用命令
  • Java 容器之 List
  • 2024年时间序列预测领域的SOTA模型总结
  • 代数结构—笔记
  • swift 开发效率提升工具
  • Oracle 数据库基础入门(四):分组与联表查询的深度探索(上)
  • 内存管理c/c++
  • 鸿蒙项目实战:PR#39888中解决的ACE引擎具体问题及技术方案赏析
  • Android 端侧运行 LLM 框架 MNN 及其应用
  • 【Linux】消息队列和信号量
  • 问题修复-后端返给前端的时间展示错误
  • Pytorch使用手册—Raspberry Pi 4 上的实时推理(30 FPS!)(专题三十六)
  • QEMU源码全解析 —— 内存虚拟化(23)
  • 语法Object.defineProperty()
  • YashanDB简介
  • Java 设计模式:软件开发的精髓与艺
  • FunPapers[3]:WWW‘25「快手」生成式回归预测观看时长
  • Makefile、Make和CMake:构建工具的三剑客
  • 字符串的原理