NOIP2013提高组 day2

1.积木大赛

题目描述
春春幼儿园举办了一年一度的“积木大赛”。今年比赛的内容是搭建一座宽度为 n n n的大厦,大厦可以看成由 n n n块宽度为 1 1 1的积木组成,第 i i i块积木的最终高度需要是 h i h_i hi

在搭建开始之前,没有任何积木(可以看成 n n n块高度为 0 0 0的积木)。接下来每次操作,小朋友们可以选择一段连续区间 [ l , r ] [l, r] [l,r],然后将第 L L L块到第 R R R 块之间(含第 L L L块和第 R R R块)所有积木的高度分别增加 1 1 1

M M M是个聪明的小朋友,她很快想出了建造大厦的最佳策略,使得建造所需的操作次数最少。但她不是一个勤于动手的孩子,所以想请你帮忙实现这个策略,并求出最少的操作次数。

输入格式
包含两行,第一行包含一个整数 n n n,表示大厦的宽度。

第二行包含 n n n个整数,第 i i i个整数为 h i h_i hi

输出格式
建造所需的最少操作数。

输入输出样例

输入 #1

5
2 3 4 1 2

输出 #1

5

说明/提示

【样例解释】

其中一种可行的最佳方案,依次选择

[ 1 , 5 ] [ 1 , 3 ] [ 2 , 3 ] [ 3 , 3 ] [ 5 , 5 ] [1,5] [1,3] [2,3] [3,3] [5,5] [1,5][1,3][2,3][3,3][5,5]

【数据范围】

对于 30 % 30\% 30%的数据,有 1 ≤ n ≤ 10 1≤n≤10 1n10

对于 70 % 70\% 70%的数据,有 1 ≤ n ≤ 1000 1 ≤ n ≤ 1000 1n1000

对于 100 % 100\% 100%的数据,有 1 ≤ n ≤ 100000 , 0 ≤ h i ≤ 10000 1 ≤ n ≤ 100000,0≤ h_i≤ 10000 1n100000,0hi10000

思路
本题考察数学推导。

分析样例有:

序号012345
高度023412

从0开始遍历,可以发现:
下一个序号对应的数字大于上一个序号对应的数字
则一定要操作下一个序号数字的次数
反之,只需操作该序号数字的次数
所以有公式:
s u m = s u m + ∑ i = 0 n − 1 ( h [ i + 1 ] > h [ i ] ) sum=sum+\sum_{i=0}^{n-1} (h[i+1]>h[i]) sum=sum+i=0n1h[i+1]>h[i]

时间复杂度为 O ( n ) O(n) On.

AC代码

#include<bits/stdc++.h> //积木大赛 
using namespace std;
int h[100005],n;
inline int read(){                            //快读 
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
int main(){
    //freopen("block.in","r",stdin);
    //freopen("block.out","w",stdout);
    int ans=0;
	n=read();
    for(int i=1;i<=n;i++) h[i]=read();
    for(int i=0;i<n;i++) if(h[i]<h[i+1]) ans+=h[i+1]-h[i];       //代公式 
	cout<<ans;
	return 0; 
}

总结
一道简单题,考试轻松AC

2.花匠

题目描述

花匠栋栋种了一排花,每株花都有自己的高度。花儿越长越大,也越来越挤。栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比较别致。

具体而言,栋栋的花的高度可以看成一列整数 h 1 , h 2 , . . . , h n ​ h_1,h_2,...,h_n​ h1,h2,...,hn。设当一部分花被移走后,剩下的花的高度依次为 g 1 , g 2 , . . . , g m g_1,g_2,...,g_m g1,g2,...,gm,则栋栋希望下面两个条件中至少有一个满足:

条件 A A A:对于所有 g 2 i > g 2 i − 1 , g 2 i > g 2 i + 1 g_{2i}>g_{2i−1},g_{2i}>g_{2i+1} g2i>g2i1,g2i>g2i+1

条件 B B B:对于所有 g 2 i < g 2 i − 1 , g 2 i < g 2 i + 1 g_{2i}<g_{2i−1},g_{2i}<g_{2i+1} g2i<g2i1,g2i<g2i+1

注意上面两个条件在 m = 1 m=1 m=1时同时满足,当 m > 1 m>1 m>1时最多有一个能满足。

请问,栋栋最多能将多少株花留在原地。

输入格式

第一行包含一个整数 n n n,表示开始时花的株数。

第二行包含 n n n个整数,依次为 h 1 , h 2 , . . . , h n h_1,h_2,...,h_n h1,h2,...,hn,表示每株花的高度。

输出格式

一个整数 m m m,表示最多能留在原地的花的株数。

输入输出样例

输入 #1

5
5 3 2 1 2

输出 #1

3

说明/提示

【输入输出样例说明】

有多种方法可以正好保留 3 3 3 株花,例如,留下第 1 1 1 4 4 4 5 5 5 株,高度分别为 5 5 5 1 1 1 2 2 2,满足条件 B B B

【数据范围】

对于 20 % 20\% 20%的数据, n ≤ 10 ; n≤10; n10;

对于 30 % 30\% 30%的数据, n ≤ 25 ; n≤25; n25;

对于 70 % 70\% 70%的数据, n ≤ 1000 , 0 ≤ h i ≤ 1000 ; n≤1000,0≤h_i≤1000; n1000,0hi1000;

对于 100 % 100\% 100%的数据, 1 ≤ n ≤ 100 , 000 , 0 ≤ h i ≤ 1 , 000 , 000 1≤n≤100,000,0≤h_i≤1,000,000 1n100,000,0hi1,000,000,所有的 h i h_i hi​随机生成,所有随机数服从某区间内的均匀分布。

思路

本题考察数学推导。
可以通过柱状图理解样例:
在这里插入图片描述

通读题后,发现 A 、 B A、B AB条件简而言之就是在 g 1 g_1 g1 g 2 g_2 g2……, g m g_m gm中,任意三个序列的中间值最大或最小,也就是在柱状图中出现波峰或波谷,那么只需要加入一个 f l a g flag flag变量记录升降趋势即可判断,每次出现都 m + + m++ m++,由于 m m m n > 1 n>1 n>1时至少为2,所以将 m m m初始化为2,即可求出答案。
时间复杂度为 O ( n ) O(n) On

AC代码

#include<bits/stdc++.h> //花匠 
using namespace std;
int h[100005],n;
inline int read(){                            //快读 
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
int main(){
    //freopen("flower.in","r",stdin);
    //freopen("flower.out","w",stdout);
    int m=2;                                  //至少两株花(n>1) 
	bool flag=0;                              //升降变量标记 
	n=read();
	if(n==1){                                 //特殊判断 
		cout<<1;
		return 0;
	}
	for(int i=1;i<=n;i++) h[i]=read();
	if(h[2]>=h[1]) flag=1;                    //升 
	for(int i=1;i<n;i++){
		if(flag){                          
			if(h[i+1]<h[i]){                  //降 
				flag=0;
				m++;
				continue;
			}
		}
		else{
			if(h[i+1]>h[i]){                  //升 
				flag=1;
				m++;
				continue;
			}
		}
	}
	cout<<m;
}

总结
比第一题稍微难做一些,但还好没有超出能力范围,经过推导就能求出答案,没有其他的算法,考试完美AC。

3.华容道

题目描述

小 B 小B B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间。

小 B 小B B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:

在一个 n × m n×m n×m棋盘上有 n × m n×m n×m个格子,其中有且只有一个格子是空白的,其余 n × m − 1 n×m−1 n×m1个格子上每个格子上有一个棋子,每个棋子的大小都是 1 × 1 1×1 1×1的;

1. 1. 1.有些棋子是固定的,有些棋子则是可以移动的;

2. 2. 2.任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。

3. 3. 3.游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。

给定一个棋盘,游戏可以玩 q q q次,当然,每次棋盘上固定的格子是不会变的, 但是棋盘上空白的格子的初始位置、 指定的可移动的棋子的初始位置和目标位置却可能不同。第 i i i次玩的时候,空白的格子在第 E X i EX_i EXi​ 行第 E Y i EY_i EYi​ 列,指定的可移动棋子的初始位置为第 S X i SX_i SXi​ 行第 S Y i SY_i SYi​列,目标位置为第 T X i TX_i TXi 行第 T Y i TY_i TYi 列。

假设 小 B 小 B B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉 小 B 小 B B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。

输入格式

第一行有 3 3 3个整数,每两个整数之间用一个空格隔开,依次表示 n , m , q n,m,q n,m,q

接下来的 n n n 行描述一个 n × m n×m n×m 的棋盘,每行有 m m m个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态, 0 0 0 表示该格子上的棋子是固定的, 1 1 1 表示该格子上的棋子可以移动或者该格子是空白的。

接下来的 q q q 行,每行包含 6 6 6 个整数依次是 E X i , E Y i , S X i , S Y i , T X i , T Y i EX_i,EY_i,SX_i,SY_i,TX_i,TY_i EXi,EYi,SXi,SYi,TXi,TYi​,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。

输出格式

q q q 行,每行包含 1 1 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出 − 1 −1 1

输入输出样例

输入 #1

3 4 2
0 1 1 1
0 1 1 0
0 1 0 0
3 2 1 2 2 2
1 2 2 2 3 2

输出 #1

2
-1

说明/提示

【输入输出样例说明】

棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。

第一次游戏,空白格子的初始位置是 ( 3 , 2 ) (3,2) (3,2)(图中空白所示),游戏的目标是将初始位置在 ( 1 , 2 ) (1, 2) (1,2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置 ( 2 , 2 ) (2,2) (2,2)(图中红色的格子)上。

移动过程如下:

在这里插入图片描述

第二次游戏,空白格子的初始位置是 ( 1 , 2 ) (1,2) (1,2)(图中空白所示),游戏的目标是将初始位置在 ( 2 , 2 ) (2,2) (2,2)上的棋子(图中绿色圆圈所示)移动到目标位置 ( 3 , 2 ) (3,2) (3,2)上。

在这里插入图片描述

要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置 ( 2 , 2 ) (2,2) (2,2)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置, 游戏无法完成。

【数据范围】

对于 30 % 30\% 30%的数据, 1 ≤ n , m ≤ 10 , q = 1 1 ≤ n, m ≤ 10,q = 1 1n,m10,q=1

对于 60 % 60\% 60%的数据, 1 ≤ n , m ≤ 30 , q ≤ 10 1 ≤ n, m ≤ 30,q ≤ 10 1n,m30,q10

对于 100 % 100\% 100%的数据, 1 ≤ n , m ≤ 30 , q ≤ 500 1 ≤ n, m ≤ 30,q ≤ 500 1n,m30,q500

95分代码

#include<bits/stdc++.h> //华容道 
using namespace std;
int dp[35][35][35][35][5],map_[35][35],flag[35][35],vis[35][35][35][35];
const int mx[4]={0,1,0,-1},my[4]={1,0,-1,0};
int ans,n,m,l,a,b,ex,ey,sx,sy,tx,ty;
struct data{
	int x,y;
};
queue<data>g;
inline int read(){                                                   //快读 
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
inline bool judge(int x,int y){                                      //判断是否可以移动         
	if(x<1||x>n||y<1||y>m||!map_[x][y]) return false;
	return true;
}
void spfa(int x,int y,int nx,int ny,int ji){                         //SPFA
    while(!g.empty())
	g.pop();
    memset(flag,0,sizeof(flag));
    g.push((data){x,y});
    dp[x][y][x][y][ji]=0;
    while(!g.empty()){
        data q=g.front();
		g.pop();
        flag[q.x][q.y]=0;
        for(int i=0;i<4;i++){
            int xx=q.x+mx[i],yy=q.y+my[i];
            if(judge(xx,yy)&&(xx!=nx||yy!=ny)){
                if(dp[x][y][xx][yy][ji]>dp[x][y][q.x][q.y][ji]+1){
                    dp[x][y][xx][yy][ji]=dp[x][y][q.x][q.y][ji]+1;
                    if(!flag[xx][yy]){
                        g.push((data){xx,yy});
                        flag[xx][yy]=1;
                    }
                }
            }
        }
    }
}
void dfs(int ex,int ey,int sx,int sy,int times){                     //dfs
	if(times>=ans) return;
	if(sx==tx&&sy==ty){
		ans=times;
		return;
	}
	if(vis[sx][sy][ex][ey]<=times) return;
	vis[sx][sy][ex][ey]=times;
	for(int i=0;i<4;i++){                                           
		int x=sx+mx[i],y=sy+my[i];
		if(judge(x,y)){
			dfs(sx,sy,x,y,times+dp[x][y][ex][ey][(i+2)%4]+1);
		}
	}
}
int main(){
    //freopen("puzzle.in","r",stdin);
    //freopen("puzzle.out","w",stdout);
    n=read(),m=read(),l=read();
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      map_[i][j]=read();
    memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(!map_[i][j]) continue;
            for(int k=0;k<4;k++){
                a=i+mx[k],b=j+my[k];
                if(judge(a,b)) spfa(i,j,a,b,k);
            }
        }
    for(int i=1;i<=l;i++){
    	memset(vis,0x3f,sizeof(vis)),ans=1e6;
		ex=read(),ey=read(),sx=read(),sy=read(),tx=read(),ty=read();
		dfs(ex,ey,sx,sy,0);
    	if(ans==1e6) printf("-1\n");
    	else printf("%d\n",ans);
	} 
}

时间复杂度应该为 O ( n 4 ) O(n^4) O(n4)

总结
这道题在考试时只能想到用深搜打暴力(图论太弱),结果只得了40分,正常打暴力理论在70分左右。正解应该是bfs+SPFA。在经过SPFA和dfs优化后达到了95分(有个样例卡常开 O 2 O_2 O2可以AC),试过了快读快写,i++变++i,加inline、resister都没过……就这样吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值