NEUQ-ACM week5

T1 P1135 奇怪的电梯

需要思考的地方 

1.显然是一个搜索题,考虑用dfs还是bfs,但是用dfs容易超时而且不太会剪枝所以最后用bfs写的(dfs代码不管怎么写都有两个点超时😥)

2.怎么同时对两种情况进行遍历(上楼还是下楼)

3.怎么存储每次的答案(用bfs的话第一次到达肯定就是最小的,不用考虑这个)

思路

好像就是普通的bfs实现思路

就是状态转移的时候要注意到达这个点的数组存的总步数是到达上一个点数组存的的步数+1,这样可以用vis数组在判断是否走过的同时储存到达这个点的总步数(虽然好像是废话)

代码(bfs和dfs)

#include<bits/stdc++.h>//dfs做法(有的点会超时)
using namespace std;
int n,a,b,ans=INT_MAX;
int cnt=0,m=0;
int k[205];
bool flag[1005];//标记有没有走过
void dfs(int floor)
{
    if(floor==b)
    {
        ans=min(cnt,ans);//保证一直取最小值
        return;
    }
    
    if(cnt>=n)return;//剪枝
    flag[floor]=true;
    for(int i=1;i>=-1;i-=2)//dfs模板
    {
        int next_floor=floor+i*k[floor];
        if(next_floor>0&&next_floor<=n&&flag[next_floor]==false)
        {
            cnt++;
            dfs(next_floor);
            flag[next_floor]=false;
            cnt--;//别 !!  漏!!
        }
    }


}
int main()
{
    cin>>n>>a>>b;
    for(int i=1;i<=n;i++)
    {
        cin>>k[i];
    }
    memset(flag,false,sizeof(flag));
    flag[0]=true;
    dfs(a);
    if(ans==INT_MAX)cout<<-1;
    else if(a==b)cout<<0;//单独考虑考虑ab相等的情况
    else cout<<ans;
}


#include<bits/stdc++.h>//bfs做法(感觉效率比较高)
using namespace std;
int k[210];
int vis[210];//存是否到过,##同时存步数##
int n,a,b;
int cur_floor,up_floor,down_floor;//目前楼层,上楼之后的楼层,下楼之后的楼层
queue<int>q;
int main()
{
    memset(vis,-1,sizeof(vis));
    cin>>n>>a>>b;
    vis[a]=0;//先把起点的标记置为0
    q.push(a);//起点入栈
    for(int i=1;i<=n;i++) cin>>k[i];//初始化每层楼操作数量
    while(!q.empty())//bfs
    {
        cur_floor=q.front();
        up_floor=cur_floor+k[cur_floor];
        down_floor=cur_floor-k[cur_floor];
        if(up_floor>=1&&up_floor<=n&&vis[up_floor]==-1)//向上走的话
        {
            q.push(up_floor);
            vis[up_floor]=1+vis[cur_floor];//储存到达这个点总步数
        }
        if(down_floor>=1&&down_floor<=n&&vis[down_floor]==-1)//向下走的话
        {
            q.push(down_floor);
            vis[down_floor]=1+vis[cur_floor];
        }
        q.pop();//进入下个状态,把上个状态pop出去
    }
    cout<<vis[b]<<endl;//这样的话不用再去求最大值,第一次到b所走过的路径必为最短路径
}

T2.P1330 封锁阳光大学

需要思考的地方

1.首先要想到这个学校可能是由多个连通图组成的,要每个图去单独判断

2.如何下手???--这个题目可以转化为把一张图染上两种颜色,相邻端点不能同色

3.转化成一个染色问题后答案就是每个联通子块中两种染色结果的最小值相加

思路

1.初始化地图

2.找每一个联通子块,对其进行染色(对每个独立的图dfs)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
vector<int>mp[N];
int color[N];//每一个点的染色
bool vis[N];//是否遍历过
int sum[2];//两种染色各自的的操作次数
bool dfs(int now,int colour)//对每个独立的图进行dfs
{
    if(vis[now])//如果已经被染过色
    {
        if(color[now]==colour)return true;//如果仍是原来的颜色,就没啥问题
        return false;//如果不是原来的颜色说明能够产生冲突
    }
    vis[now]=true;//记录走过
    sum[color[now]=colour]++;//这一种颜色的个数+1,这个点的颜色也记录下来
    bool flag=true;//是否可行
    for(int i=0;i<mp[now].size();i++)//对边进行遍历
    {
        int next=mp[now][i];
        flag=(flag&&dfs(next,1-colour));//是否可以继续染色
    }
    return flag;//返回染色是否完成
}
int main()
{
    int flag=1,ans=0;
    cin>>n>>m;
    memset(vis,false,sizeof(vis));
    memset(color,0,sizeof(color));
    for(int i=1;i<=m;i++)
    {
        int u,v;
        cin>>u>>v;
        mp[u].push_back(v);
        mp[v].push_back(u);
    }                       //以上都是初始化
    for(int i=1;i<=n;i++)
    {
        if(vis[i])continue;//如果此点已被包含为一个已经被遍历过的子图,则不需重复遍历
        sum[0]=sum[1]=0;//初始化
        if(!dfs(i,0))//如果不能染色
        {
            flag=0;
            break;
        }
        ans+=min(sum[0],sum[1]);//ans加上其中小的那个
    }
    if(!flag)cout<<"Impossible";
    else cout<<ans;
}

T3.P3916 图的遍历

需要思考的问题 

1.看到数据是1e5的级别,感觉正常做会超时,所以要考虑怎么存图(反向建边)

2.搜索方式用什么,准确的说,怎么遍历每个点的顺序

思路

1.初始化地图,反向建边减少搜索次数

2.从最大的点开始往前搜索,这样他能到达的点的能达到值最大的点就是自己

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int u,v,m,n;
vector<int>mp[N];
int biggest[N];//也可以认为存的是每个节点可达的最大节点
void dfs(int x,int d)//从x开始,尽头是d,即可以从x到达d
{
    if(biggest[x]) return;
    biggest[x]=d;
    for(int i=0;i<mp[x].size();i++)
        dfs(mp[x][i],d);
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>u>>v;
        mp[v].push_back(u);//反向建边,优点是:递归由大向小,减少搜索次数 
    }                      //反向建立边的作用相当于让之前的路径有可以反悔的余地。
    for(int i=n;i>0;i--)
        dfs(i,i);
    for(int i=1;i<=n;i++)
        cout<<biggest[i]<<' ';
    cout<<endl;
}

T4.P8604 [蓝桥杯 2013 国 C] 危险系数 

需要思考的问题

1.危险系数到底是啥?-就是两个点之间所有路径都要经历这个点

2.怎么搜索-dfs

3.差不多就这样😛

思路

1.建图

2.从起点开始dfs,记录到终点路径上所有的点

3.经过一次这个点就把vis里面这个点对应的序号++

4.最后看总共有n条路径可以联通这两点,在vis数组里找被用过n次的点就是危险系数之一

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int u,v,st,ed,n,m;
int cnt=0;
vector<int>mp[N];//用vector存图
bool vis[N];//是否来过
int total[N];//这个点被用了几次
void dfs(int now) 
{
    if(now==ed)
    {
        cnt++;
        for(int i=1;i<=n;i++)//统计用到了哪些点
        {
            if(vis[i])
             total[i]++;   
        }
        return;
    }
    for(int i=0;i<mp[now].size();i++)//注意遍历方式
    {
        int next=mp[now][i];
        if(!vis[next])
        {
            vis[next]=true;
            dfs(next);
            vis[next]=false;
        }
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)//注意要从一开始,根据题意最前面的点编号为1
    {
        cin>>u>>v;
        mp[u].push_back(v);//
        mp[v].push_back(u);//初始化图
    }
    cin>>st>>ed;
    vis[st]=true;//先把起点标记为走过
    dfs(st);

    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(total[i]==cnt)//代表从st到ed不管怎么走都会经过他
            ans++;
    }
    cout<<ans-2;//最后要减去起点和终点
}

T5 P4961 小埋与扫雷

 需要思考的问题

总之就是把题目看懂,什么叫连通块什么叫空格balabala

思路

概括地说,就是把问题拆分成几个小函数,比如用来判断连通块的函数,判断是空格还是数字的函数,最后在主函数一起调用就可以了

代码

#include<bits/stdc++.h>
using namespace std;
int mp[1005][1005]={0};//1:雷,2:空格,3:数字 
bool vis[1005][1005];
int plus_x[8]={1,1,1,0,0,-1,-1,-1};
int plus_y[8]={1,0,-1,1,-1,1,0,-1};
int n,m,ans;

void find(int x,int y)//判断是数字还是空格
{
    bool boom=false;//是否有雷,有雷的话就不用再扫下去了
    for(int i=0;i<8&&!boom;i++)
    {
        int x_next=x+plus_x[i];
        int y_next=y+plus_y[i];
        if(x_next>=0&&x_next<n&&y_next>=0&&y_next<m)
        {
            if(mp[x_next][y_next]==1)
                boom=true;
        }

    }
    if(!boom)mp[x][y]=2;//是空格
    else mp[x][y]=3;//是数字
}

void dfs(int x,int y)//判断八连通块个数
{
    vis[x][y]=true;
    for(int i=0;i<8;i++)
    {
        int x_next=x+plus_x[i];
        int y_next=y+plus_y[i];
        if(x_next>=0&&x_next<n&&y_next>=0&&y_next<m)
        {
            if(mp[x_next][y_next]==2&&!vis[x_next][y_next])
                dfs(x_next,y_next);//不用回溯
        }
    }
}

bool check(int x,int y)//周围有没有空格
{
    bool st=true;
    for(int i=0;i<8&&st==true;i++)
    {
        int x_next=x+plus_x[i];
        int y_next=y+plus_y[i];
        if(x_next>=0&&x_next<n&&y_next>=0&&y_next<m)
        {
            if(mp[x_next][y_next]==2) 
                st=false;
        }
    }
    return st;
}

int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)//初始化地图
    {
        for(int j=0;j<m;j++)
        cin>>mp[i][j];
    }
    for(int i=0;i<n;i++)//判断数字还是空格
    {
        for(int j=0;j<m;j++)
        {
            if(!mp[i][j])//是0才判断
                find(i,j);
        }
    }
    for(int i=0;i<n;i++)//找连通块
    {
        for(int j=0;j<m;j++)
        {
           if(mp[i][j]==2&&!vis[i][j])
           {
                dfs(i,j);
                ans++;
           }
        }

    }

    for(int i=0;i<n;i++)//周围没有空格的数字
    {
        for(int j=0;j<m;j++)
        {
            if(mp[i][j]==3)
            {
                if(check(i,j))
                    ans++;
            }
        }

    }
    cout<<ans;
}

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值