G题:Red and Black

 

代码: 

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int w,h,num;
char a[30][30];
int dirx[4]={1,-1,0,0};
int diry[4]={0,0,1,-1};//定义向右,向左,向上,向下的坐标
void DFS(int x,int y)
{
    a[x][y]='#';//因为把起点已经算进去了,所以为防止之后再多算起点,把起点变成#
    num++;
    int xx,yy;
    for(int k=0;k<4;k++)
    {
        xx=x+dirx[k];
        yy=y+diry[k];
        if(a[xx][yy]=='.'&&xx>=0&&xx<h&&yy>=0&&yy<w)
        {
            DFS(xx,yy);
        }
    }
}
int main()
{
    int i,j,di,dj;
    while(scanf("%d%d",&w,&h)!=EOF)
    {
        getchar();
        if(w==0&&h==0)
            break;
        for(i=0;i<h;i++)
        {
            for(j=0;j<w;j++)
            {
                scanf("%c",&a[i][j]);
                if(a[i][j]=='@')
                {
                    di=i;
                    dj=j;
                }
            }
            getchar();
        }
        num=0;
        DFS(di,dj);
        printf("%d\n",num);
    }
    return 0;
}

 

本题是常规的BFS和DFS问题,因为之前DFS用的比较熟悉了(I、H用的都是深度优DFS先算法),所以就DFS,有个博主写的这个题的多解之后可以下去学习参考。

https://blog.csdn.net/hurmishine/article/details/50927317

J 题:Nightmare

代码: 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
char mapp[16][16];
int vis[16][16];
int dirx[4]={-1,0,1,0};
int diry[4]={0,-1,0,1};
int n,m,f_x,f_y;//n行m列,f_x,f_y为出口的横纵坐标
struct maze//坐标结构体存储总时间和炸弹倒计时
{
    int x,y;
    int to_time,now_time;
};
bool judge(int x,int y)//判断越界
{
    if(x>=n||x<0||y>=m||y<0)
        return false;
    if(mapp[x][y]=='0')
        return false;
    return true;
}
int BFS(int x,int y)
{
    queue<maze> q;
    maze a,b;
    memset(vis,0,sizeof(vis));
    vis[x][y]=6;//记录之前到达某点的剩余时间
    a.x=x;
    a.y=y;
    a.to_time=0;
    a.now_time=6;
    q.push(a);
    while(!q.empty())
    {
        a=q.front();
        q.pop();
        if(a.x==f_x&&a.y==f_y)
            return a.to_time;
        for(int i=0;i<4;i++)
        {
            b.x=a.x+dirx[i];
            b.y=a.y+diry[i];
            if(judge(b.x,b.y))
            {
                if(mapp[b.x][b.y]=='4')//如果到了重置时间
                {
                    if(a.now_time==1)
                        continue;
                    else
                    {
                        b.now_time=6;
                        b.to_time=a.to_time+1;
                    }
                    if(b.now_time==0||b.now_time<=vis[b.x][b.y])// 判断后到达的此点剩余时间是否 大于 之前到达这个点剩余时间
                        continue;
                    else//更新时间
                    {
                        vis[b.x][b.y]=b.now_time;
                        q.push(b);
                    }
                }
                else
                {
                    b.now_time=a.now_time-1;
                    b.to_time=a.to_time+1;
                    if(b.now_time==0||b.now_time<=vis[b.x][b.y])// 判断后到达的此点剩余时间是否 大于 之前到达这个点剩余时间
                        continue;
                    else//更新时间
                    {
                        vis[b.x][b.y]=b.now_time;
                        q.push(b);
                    }
                }
            }
        }
    }
    return -1;
}
int main()
{
    int T;
    int s_x,s_y;//入口横纵坐标
    cin>>T;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        getchar();
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                scanf("%c",&mapp[i][j]);//输入坐标,但要注意要把空格和回车吞掉。
                getchar();
                if(mapp[i][j]=='2')//输入时记录起点坐标
                {
                    s_x=i;
                    s_y=j;
                }
                if(mapp[i][j]=='3')//输出时记录终点坐标
                {
                    f_x=i;
                    f_y=j;
                }
            }
        }
        cout<<BFS(s_x,s_y)<<endl;
    }
    return 0;
}

 

本题是一道运用BFS算法的题,与之前用的DFS算法不同,在写BFS的过程中,我们是用队列把每一次生成的新的步骤数组入队,然后再检测是否队空不空继续搜索,直到出现目标或者没有返回-1,和DFS通过递归进行搜索找到终点的方式不同。

简单翻译一下这个题意:

以下是一些规则:

1.我们可以假设迷宫是2个数组。

2. 伊格内修斯每分钟只能走到最近的一个地区,他不应该走出边境,当然他也不能在墙上走。

3.如果伊格内修斯在爆炸时间变为0时到达出口,他就无法走出迷宫。

4. 如果伊格内修斯在爆炸时间变为0时到达装有炸弹静止装置的区域,他就不能使用该装置复位炸弹。

5. 一个炸弹重置设备可以用多少次就用多少次,如果需要的话,伊格内修斯可以用多少次就能到达迷宫中的任何地方。

6. 爆炸时间复位时间可以忽略,也就是说,如果Ignatius到达一个含有炸弹静止装置的区域,且爆炸时间大于0,则爆炸时间复位为6。

输入包含几个测试用例。输入的第一行是单个整数T,它是测试用例的数量。接下来是T测试用例。

每个测试用例以两个整数N和M(1<=N,Mm=8)开始,这两个整数表示迷宫的大小。然后N行,每一行包含M个整数。该数组指示迷宫的布局。

迷宫中有5个整数表示不同类型的面积:

0:这是一堵墙,伊格那丢不应该在上面走。

1这个地方什么都没有,伊格那丢可以在上面走。

2:伊格那丢的起始位置,伊格那丢开始从这个位置逃跑。

3:迷宫的出口,伊格那丢的目标位置。

4:该区域有一个炸弹复位设备,Ignatius可以步行到这些区域来延迟爆炸时间。

分析:

NightMare  就是在一个地宫中从入口到出口,2为入口,3为出口,0为墙,1为路。

当从入口离开,地宫倒计时6秒坍塌,地图中有一些装置可以重置坍塌时间,(不是坍塌时间晚6秒,而是重新更新到6秒,继续倒计时)

最终输出到出口时,花费的总时间。

有几点:

①当你踏上出口时,倒计时刚好归0,则无法出去。同理,倒计时归0时,到达重置时间点也是不可以的。

②如果出不去 输出-1.

③重置时间装置可以重复触发。

这道题目关键就在于它的VIS数组,因为里面的路可以多次访问,不能走一次都归0。

当碰到重置时间装置,使vis数组全为0,也是行不通的,因为队列里有一些其他的点的访问会影响到。

仔细,想想,什么时候不需要再次走这条路了呢?

当然是当你到达这个点时,和之前到达此点所剩余时间比较,

如果你后到达这个点剩余时间小于之前到达这个点剩余时间,那就不需要走这条路了。

剩余时间,当然是地宫坍塌的时间,如此设置vis数组,bfs就可以过了。

 L 题:Sticks 

 

代码: 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,maxx,sum,arr[85];
bool ipos,vis[85];
bool cmp(int a,int b)//由大到小排序
{
    return a>b;
}
void DFS(int res,int js)
{
    if(ipos==1)// 找到答案,返回
        return;
    if(js==n)//如果所有都被选择 ispos置1,表示找到
    {
        ipos=1;
        return;
    }
    if(res==0&&js<n)// 如果res=0 且并非所有都被选择,则继续广度搜索
        DFS(maxx,js);
    for(int i=0;i<n;i++)
    {
        if(!ipos&&!vis[i]&&arr[i]<=res)
        {
            vis[i]=1;
            DFS(res-arr[i],js+1);
            vis[i]=0;
            if(res==arr[i])// 第一种剪枝,这个去掉 +40MS
                return;
            if(res==maxx)//第二种剪枝,这个去掉,TLE。
                return;
            while(arr[i]==arr[i+1])// 第三种剪枝,这个剪枝去掉了,+100MS
                i++;
        }
    }
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)
            break;
        sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&arr[i]);
            sum+=arr[i];
        }
        sort(arr,arr+n,cmp);
        maxx=arr[0];
        if(maxx>sum-maxx)// 剪枝:如果最长的长度大于剩余所有长度和,则答案就是他们的和.
        {
            printf("%d\n",sum);
            continue;
        }
        ipos=0;
        while(!ipos)
        {
            while(sum%maxx!=0)// 剪枝:如果所有长度总和对目标长度取模不为0,则目标长度一定不是答案,
                maxx++;
            memset(vis,0,sizeof(vis));
            DFS(maxx,0);
            if(ipos)
                break;
            maxx++;
        }
        printf("%d\n",maxx);
    }
    return 0;
}

 

本题是深度搜索与剪枝结合的题,如果不做剪枝处理,则会出现超时问题,下面有几点说明

  1. dfs函数中那三个if里的剪枝:

第一条剪枝:

如果你剩余的长度和当前长度相等,就没有必要搜索,也就是说当你剩余长度为3,接着搜索3,发现不符合,就不需要搜索剩下能构成3的(比如2+1,1+1+1等)

第二条剪枝:(重要的)

意思是如果剩余的长度等于绳子总长度,而当前不符合,就不需要接着搜索。

也就是说,接下来搜索的长度就是绳子目标长度而你当前长度根本用不上,那就肯定不符合了。

例子:  假设 要搜索目标长度为 7  :绳长有 7 6 3 2 2.

假设 第一个7 搜索完,接下来搜索6 发现6没有1来配对,程序会接下来搜索 3 2 2,但很明显根本不需要搜索后面的,前面有一个用不上,后面就不需要再搜索了。

第三条剪枝:

如果当前长度的不满足,那么相同长度的就不需要再次去搜索

2.主函数中有sum%maxx!=0这个语句,理解为当我们所设定棍子折断前最小长度无论是多少,sum都是这些棍子折断前长度的倍数,所以取这些搜索。

3.深度搜索搜索到目标的判定条件:

if(!ipos&&!vis[i]&&arr[i]<=res)

{

    vis[i]=1;

    DFS(res-arr[i],js+1);

    vis[i]=0;

正如代码写的,一个棒子无论被怎样折断,其假设的总长res(第一次是Maxx)-arr[i]肯定在其他小棒中能找到这样的长度,如果这样的小棒又被分成了几份,一定能找到它的子份再进行分解,直到res变为0,则表示这个分支搜索成功,则对下一个分支进行搜索,即DFS(maxx,js)。Vis[i]表示你再进去搜索前标记为1,如果成功找到,标记1则会让其他搜索避开,减少重复搜索,如果没有找到,则再变回0,让其他分支去搜索这个路径。

 B题: Moving Tables

 

 

代码: 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int N,s,t,coun[500];
int main()
{
    int T,k;
    cin>>T;
    while(T--)
    {
        scanf("%d",&N);
        memset(coun,0,sizeof(coun));
        for(int i=0;i<N;i++)
        {
            scanf("%d%d",&s,&t);
            if(s>t)//为保证是从小房间号起,可以将大的起点和小点终点交换一下
            {
                k=s;
                s=t;
                t=k;
            }
            if(s%2==0)//对于终点和起点的要求为了保证在路途中把所有的房间号全部找全,所以要求起点为奇数
                s-=1;//不为奇数的要-1
            if(t%2==1)//要求终点为偶数,不为偶数的,终点要加1
                t+=1;
            for(int i=s;i<=t;i++)
               coun[i]+=10;
        }
        printf("%d\n",*max_element(coun,coun+500));
    }
    return 0;
}

 

本题是按照固定规则寻找最优路径的一道题,属于贪心算法的范畴

思路:若移动多个桌子时,所需要经过的走廊没有重合处,即可以同时移动。若有一段走廊有m个桌子都要经过,一次只能经过一个桌子,则需要m*10的时间移动桌子。设一个数组,下标值即为房间号。桌子经过房间时,该房间号为下标对应的数组值即加10。最后找到最大的数组值,即为移动完桌子需要的最短时间。

利用每次移动时,用数组下标代表房间号,每经过一个房间,对应数组加10,之后若遇到途径该房间或从该房间开始或结束,则会累加计时,达到模拟题中说的一个走廊每次过一个桌子的过程。如果没有重合,则每个数组内容只加10,最后找数组里最大的数,即为所花费的最小时间,模拟了不重合时同时搬桌子的过程

 E题: Parencodings

 

代码: 

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,s[100];
int countt;
bool flag;
int main()
{
    int t,p,num;
    cin>>t;
    while(t--)
    {
        scanf("%d",&n);
        memset(s,0,sizeof(s));//初始化,假设全部都是左括号,0表示
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&p);
            s[p+i]=1;//右括号用1表示
        }
        for(int i=1;i<=2*n;i++)
        {
            if(s[i]==1)
            {
                if(s[i-1]==0)
                {
                    printf("1");
                    if(i!=2*n)
                        printf(" ");
                }
                else
                {
                    flag=1;
                    countt=1;
                    int j=i-1;
                    num=0;
                    while(flag)
                    {
                        if(s[j]==1)
                        {
                            countt++;
                            j--;
                        }
                        else
                        {
                            if(countt)
                            {
                                countt--;
                                j--;
                                num+=1;
                            }
                            if(countt==0)
                                flag=0;
                        }
                    }
                    printf("%d",num);
                    if(i!=2*n)
                        printf(" ");
                }
            }
        }
        printf("\n");
    }
    return 0;
}

 这题是一道括号匹配题,难点在于入手的时候如何重新构建s字符串。我的思路是我们可以由P数组先求出S数组,再由S数组找出W数组会更容易些。至于怎么由P数组找出S呢,我们用0和1分别表示左括号和右括号,先把S数组初始化为0(即初始化为只包含左括号),那么对于每一个输入的Pi,由定义我们知道,这是第i个右括号,同时我们还知道,这个右括号前面有Pi个左括号,这时我们就可以对S[ Pi+i ]赋值为1,即该点为右括号。有了S数组,找W数组就是根据当前数组的数值和前一个数组的数值来判断括号个数,若前一个为左括号,则直接直接输出为1,否则先记录下右括号个数,再往前倒推找左括号,直到找到相应的左括号为止就行

K题: Defragment 

 

代码: 

#include <iostream>
#include <stack>

using namespace std;

int main() {
    int N;
    int K;
    int move_cnt = 0;
    cin >> N >> K;

    int * clusters = new int[N + 1];
    for (int i = 0; i <= N; ++i) {
        clusters[i] = 0;
    }
    int sum = 0;
    for (int i = 0; i < K; ++i) {
        int n;
        cin >> n;
        for (int j = 1; j <= n; ++j) {
            int a;
            cin >> a;
            clusters[a] = ++sum;
        }
    }
    for (int i = 1; i <= N; ++i) {
        if (clusters[i] == 0 || clusters[i] == i) {
            continue;
        }
        stack<int> s;
        int next = clusters[i];
        s.push(i);
        bool isCircle = false;
        while (true) {
            if (clusters[next] == i) {
                isCircle = true;
                break;
            } else if (clusters[next] == 0) {
                break;
            }
            s.push(next);
            next = clusters[next];
        }
        if (isCircle == true) {
            int j = N;
            while (clusters[j] != 0) {
                --j;
                continue;
            }
            cout << next << " " << j << endl;
            clusters[j] = clusters[next];
            int t;
            while (!s.empty()) {
                t = s.top();
                cout << t << " " << next << endl;
                clusters[next] = clusters[t];
                next = t;
                s.pop();
                ++move_cnt;
            }
            clusters[next] = clusters[j];
            clusters[j] = 0;
            cout << j << " " << next << endl;
        } else {
            int t;
            while (!s.empty()) {
                t = s.top();
                cout << t << " " << next << endl;
                clusters[next] = clusters[t];
                next = t;
                s.pop();
                ++move_cnt;
            }
            clusters[next] = 0;
        }
    }
    if (move_cnt == 0) {
        cout << "No optimization needed" << endl;
    }
    return 0;
}

光是看题都让人看得绝望

附上博客网址,慢慢消化:

https://www.cnblogs.com/dengeven/p/3230223.html

 

 

 

 

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐