目录
A - Lake Counting
题意:
假设在(x ,y)处有一块水源,如果在(x+1 ,y)、(x ,y+1)、(x-1 ,y)、(x ,y-1)、(x+1 ,y+1)、(x+1 ,y-1)、(x-1 ,y+1)、(x-1 ,y-1)处也有一块水源,我们认为这其实是一块水源。
现给出水源的分布图,请给出图中共有多少块不同的水源?
思路:
是不是感觉题目很眼熟?
这次你可以相信你所看到的,和阶段二Day1的F题完全一致的题,描述不同,符号不同而已。
-
考察点:搜索,DFS
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<string.h>
#include<cstdlib>
#include<fstream>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
string mp[105];
int vis[105][105];
int ans;
int n,m;
int dx[8]= {0,0,1,-1,1,-1,-1,1};
int dy[8]= {1,-1,1,-1,0,0,1,-1};
void dfs(int x,int y,int id){
vis[x][y]=id;
for(int i=0;i<8;i++){
int xx=x+dx[i];
int yy=y+dy[i];
if(xx>=n||xx<0||yy>=m||yy<0) continue;
if(!vis[xx][yy]&&mp[xx][yy]=='W'){
vis[xx][yy]=id;
dfs(xx,yy,id);
}
}
}
int main()
{
while(cin>>n>>m){
for(int i=0;i<n;i++)
cin>>mp[i];
memset(vis,0,sizeof(vis));
ans=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
if(!vis[i][j]&&mp[i][j]=='W')
{
ans++;
dfs(i,j,ans);
}
}
cout<<ans<<endl;
}
return 0;
}
B - Binary Search
题意:
给定长度为n的数组a,再给出一个长度为q的数组b,请输出b中有多少元素出现在a数组中。
思路:
混在搜索专题的二分水题。
再让我发现哪个大聪()明()暴力直接搜,我tm********************!
-
考察点:二分,STL
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<string.h>
#include<cstdlib>
#include<fstream>
#include<queue>
#include<stack>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
int a[maxn];
int main()
{
ios::sync_with_stdio(false);
int n,q,x;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
cin>>q;
int cnt=0;
for(int i=0;i<q;i++){
cin>>x;
int pos=lower_bound(a,a+n,x)-a;
if(a[pos]==x) cnt++;
}
cout<<cnt<<endl;
}
C - Fire!
题意:
你现在正身处在一个R*C的火场之中,火场内有‘#’(墙),‘F’(着火点),‘.’(还没着火的地方)。你要做的只有一件事:活下去!
你每秒可以向上下左右四个方向移动一格,而每个着火点每秒都会向上下左右四个方向同时蔓延火势。你只需要跑到火场的边缘即可逃生。
如果你可以活下来,请输出最短时间;
如果不能,输出“IMPOSSIBLE”,并跟着21年的IG一起寄了吧。
思路:
如果同时对人和火进行BFS,如此复杂的情况会让我们de上半天bug。 (如果你是大佬,无视这一句,不,请无视这篇博客)
我们先对火进行BFS,算出每个空地着火的最短时间。我们只需要在着火之前到达此点即可视为安全。
结束条件就是到达地图边界,最短时间为到达时间+1。(你还要跑出去啊,那也算一步)
-
考察点:搜索,BFS
代码:
#include<iostream>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int inf = 0x3f3f3f3f;
int T, r, c, ans, sx, sy;
char mp[1007][1007];
int vis[1007][1007], t[1007][1007];
struct node{
int x, y;
int step;
}nw, nxt;
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
void bfs1() {
queue<node> q;
for(int i = 0 ; i< r; i++) {
for(int j = 0; j < c; j++) {
if(mp[i][j] == 'F') {
nw.x = i, nw.y = j;
t[i][j] = 0;
q.push(nw);
}
}
}
while(!q.empty()) {
nw = q.front(), q.pop();
for(int i = 0; i < 4; i++) {
nxt.x = nw.x + dx[i], nxt.y = nw.y + dy[i];
if(nxt.x >= 0 && nxt.x < r && nxt.y >= 0 && nxt.y < c && mp[nxt.x][nxt.y] != '#' && t[nxt.x][nxt.y] > t[nw.x][nw.y] + 1) {
t[nxt.x][nxt.y] = t[nw.x][nw.y] + 1;
q.push(nxt);
}
}
}
}
void bfs2(int x, int y) {
nw.x = x, nw.y = y, nw.step = 0;
vis[x][y] = 1;
queue<node> q;
q.push(nw);
while(!q.empty()) {
nw = q.front(), q.pop();
if(nw.x ==0 || nw.x == r - 1 || nw.y == 0 || nw.y == c - 1) {
ans = nw.step + 1;
return;
}
for(int i = 0; i < 4; i++) {
nxt.x = nw.x + dx[i], nxt.y = nw.y + dy[i];
if(nxt.x >= 0 && nxt.x < r && nxt.y >= 0 && nxt.y <c && mp[nxt.x][nxt.y] != '#' && nw.step + 1 < t[nxt.x][nxt.y] && vis[nxt.x][nxt.y] == 0) {
vis[nxt.x][nxt.y] = 1;
nxt.step = nw.step + 1;
q.push(nxt);
}
}
}
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d%d", &r, &c);
for(int i = 0; i < r; i++) {
scanf("%s", mp[i]);
for(int j = 0; j < c; j++) {
if(mp[i][j] == 'J') {
sx = i, sy = j;
}
}
}
memset(vis, 0, sizeof(vis));
memset(t, inf, sizeof(t));
ans = inf;
bfs1();
bfs2(sx, sy);
if(ans >= inf) printf("IMPOSSIBLE\n");
else printf("%d\n", ans);
}
return 0;
}
D - Find a way
思路:
-
考察点:搜索,BFS
E - Power Calculus
题意:
给定数字n,问我们需要操作多少次才能把x^1变为x^n?
每次操作可以 乘 / 除 现已算出的数字。
例如:
一开始只有x^1,所以我们只能让x^1 乘 / 除 x^1。此时我们让x^1变为x^2。
我们现在有x^1与x^2,所以我们可以让x^2 乘 / 除 两者之中的其中一个。
思路:
深搜,但需要剪枝。
首先我们不去存储x的n次方,就算x=2,x的1000次方也是你无能为力的对手。
高中(或是初中)数学公式:
X^n × X^m = X^(n+m)
我们只需要存储当前的指数即可。
如何减小时间复杂度:
- 运用昨天所涉及到的IDA*,每次限制操作的次数。
- 每次操作最快的增长方式:自己×自己。
假设一开始为x^2,
第1次增长:2+2=4(此处数字代表指数变化)
第2次增长:4+4=8
第3次增长:8+8=16
.......
第n次增长:2 × 2^n
对于当前的次数,如果令其每次使用最大次数增长方式仍无法在规定次数内达到n次方则放弃。
具体看代码。
-
考察点:数学,搜索,DFS,IDA*
代码:
#include<iostream>
#include<cstdio>
using namespace std;
int vis[2050]; //vis[i]:操作第i次时到达了x的vis[i]次方
int deep,flag,n;
void dfs(int n_deep)
{
if(flag) return;
if(n_deep>deep) return; //当前操作次数已超过规定次数
if((vis[n_deep]<<(deep-n_deep))<n) return; //指数相加,爆炸增长依然无法到达n,放弃
if(vis[n_deep]==n){
flag=1;
return;
}
for(int i=1;i<=n_deep;i++){
int t=vis[n_deep]+vis[i]; //乘法
if(t>0&&t<2000)
{
vis[n_deep+1]=t;
dfs(n_deep+1);
}
t=vis[n_deep]-vis[i]; //除法
if(t>0&&t<2000)
{
vis[n_deep+1]=t;
dfs(n_deep+1);
}
}
}
int main(){
while(~scanf("%d",&n)&&n){
deep=0;
flag=0;
vis[1]=1;
while(!flag){
deep++;
dfs(1);
}
printf("%d\n",deep-1);
}
}
F - Find The Multiple
思路:
-
考察点:数学,搜索,BFS
G - 棋盘问题
题意:
虽然每次都这么说,但是:中文题你问我题意?
思路:
跟八皇后问题比较相似,我们需要记录每一行/列是否有棋子,每次放置选择一列/行进行放置,并且规定当第k个棋子放在第i列时,第k+1个棋子需要放置在第i+1列~第n列。
接下来就是简单的模拟了。
-
考察点:搜索,DFS,模拟
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<string.h>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
string mp[15];
bool vis[15]; //vis[i]:第i列是否可用
int n,k,ans;
void dfs(int r,int num)
{
for(int i=0; i<n; i++)
{
if(mp[r][i]=='#'&&!vis[i])
{
if(num==1)
{
ans++;
}
else
{
vis[i]=true;
for(int j=r+1; j<=n-num+1; j++)
dfs(j,num-1);
vis[i]=false;
}
}
}
}
int main()
{
while(cin>>n>>k)
{
if(n==-1&&k==-1)
break;
for(int i=0; i<n; i++)
cin>>mp[i];
ans=0;
memset(vis,false,sizeof(vis));
for(int i=0; i<=n-k; i++)
dfs(i,k);
cout<<ans<<endl;
}
}