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 1≤n≤10;
对于 70 % 70\% 70%的数据,有 1 ≤ n ≤ 1000 1 ≤ n ≤ 1000 1≤n≤1000;
对于 100 % 100\% 100%的数据,有 1 ≤ n ≤ 100000 , 0 ≤ h i ≤ 10000 1 ≤ n ≤ 100000,0≤ h_i≤ 10000 1≤n≤100000,0≤hi≤10000。
思路
本题考察数学推导。
分析样例有:
序号 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
高度 | 0 | 2 | 3 | 4 | 1 | 2 |
从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=0n−1(h[i+1]>h[i])
时间复杂度为 O ( n ) O(n) O(n).
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>g2i−1,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<g2i−1,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; n≤10;
对于 30 % 30\% 30%的数据, n ≤ 25 ; n≤25; n≤25;
对于 70 % 70\% 70%的数据, n ≤ 1000 , 0 ≤ h i ≤ 1000 ; n≤1000,0≤h_i≤1000; n≤1000,0≤hi≤1000;
对于 100 % 100\% 100%的数据, 1 ≤ n ≤ 100 , 000 , 0 ≤ h i ≤ 1 , 000 , 000 1≤n≤100,000,0≤h_i≤1,000,000 1≤n≤100,000,0≤hi≤1,000,000,所有的 h i h_i hi随机生成,所有随机数服从某区间内的均匀分布。
思路
本题考察数学推导。
可以通过柱状图理解样例:
通读题后,发现 A 、 B A、B A、B条件简而言之就是在 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) O(n)
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×m−1个格子上每个格子上有一个棋子,每个棋子的大小都是 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 1≤n,m≤10,q=1;
对于 60 % 60\% 60%的数据, 1 ≤ n , m ≤ 30 , q ≤ 10 1 ≤ n, m ≤ 30,q ≤ 10 1≤n,m≤30,q≤10;
对于 100 % 100\% 100%的数据, 1 ≤ n , m ≤ 30 , q ≤ 500 1 ≤ n, m ≤ 30,q ≤ 500 1≤n,m≤30,q≤500。
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){ //SPFAwhile(!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){ //dfsif(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都没过……就这样吧。
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 1≤n≤10;
对于 70 % 70\% 70%的数据,有 1 ≤ n ≤ 1000 1 ≤ n ≤ 1000 1≤n≤1000;
对于 100 % 100\% 100%的数据,有 1 ≤ n ≤ 100000 , 0 ≤ h i ≤ 10000 1 ≤ n ≤ 100000,0≤ h_i≤ 10000 1≤n≤100000,0≤hi≤10000。
思路
本题考察数学推导。
分析样例有:
序号 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
高度 | 0 | 2 | 3 | 4 | 1 | 2 |
从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=0n−1(h[i+1]>h[i])
时间复杂度为 O ( n ) O(n) O(n).
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>g2i−1,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<g2i−1,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; n≤10;
对于 30 % 30\% 30%的数据, n ≤ 25 ; n≤25; n≤25;
对于 70 % 70\% 70%的数据, n ≤ 1000 , 0 ≤ h i ≤ 1000 ; n≤1000,0≤h_i≤1000; n≤1000,0≤hi≤1000;
对于 100 % 100\% 100%的数据, 1 ≤ n ≤ 100 , 000 , 0 ≤ h i ≤ 1 , 000 , 000 1≤n≤100,000,0≤h_i≤1,000,000 1≤n≤100,000,0≤hi≤1,000,000,所有的 h i h_i hi随机生成,所有随机数服从某区间内的均匀分布。
思路
本题考察数学推导。
可以通过柱状图理解样例:
通读题后,发现 A 、 B A、B A、B条件简而言之就是在 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) O(n)
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×m−1个格子上每个格子上有一个棋子,每个棋子的大小都是 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 1≤n,m≤10,q=1;
对于 60 % 60\% 60%的数据, 1 ≤ n , m ≤ 30 , q ≤ 10 1 ≤ n, m ≤ 30,q ≤ 10 1≤n,m≤30,q≤10;
对于 100 % 100\% 100%的数据, 1 ≤ n , m ≤ 30 , q ≤ 500 1 ≤ n, m ≤ 30,q ≤ 500 1≤n,m≤30,q≤500。
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){ //SPFAwhile(!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){ //dfsif(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都没过……就这样吧。