HRBU 2021暑期训练解题报告阶段二Day2

目录

        A - Lake Counting

        B - Binary Search

        C - Fire!

        D - Find a way

        E - Power Calculus

        F - Find The Multiple

        G - 棋盘问题


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题完全一致的题,描述不同,符号不同而已。

具体请看HRBU 2021暑期训练解题报告阶段二Day1

  • 考察点:搜索,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) 

我们只需要存储当前的指数即可。

如何减小时间复杂度:

  1. 运用昨天所涉及到的IDA*,每次限制操作的次数。
  2. 每次操作最快的增长方式:自己×自己。

假设一开始为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;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_45750296/article/details/119570596