acwing蓝桥杯c++A/B组辅导课--第六讲双指针、BFS与图论

1.日志统计

法1:

用双指针i,j枚举时间段,如果i-j>=d,就将超出范围的cnt[]--,且j++,这样就会始终有一个长度为d的区间

vector<int> f[t] 记录了在t时刻里获赞的所有编号

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
vector<int> f[N];//f[t]记录了所有在t时刻获赞的编号
int cnt[N];//cnt[id]表示id的获赞数,是动态的,超过了时间长度d会--
bool st[N];//st[id]判断编号id是不是热帖
int main()
{
    int n,d,k;
    cin>>n>>d>>k;
    for(int i=0;i<n;i++)
    {
        int ts,id;
        cin>>ts>>id;
        f[ts].push_back(id);
    }
    for(int i=0,j=0;i<=1e5;i++)//i,j维护一个长度为d的时间区间
    {
        if(i-j>=d)//长度超过d了,原来j时刻的那些获赞就会--
        {
            for(auto x:f[j])
            {
                cnt[x]--;
            }
            j++;
        }
        for(auto x:f[i])//遍历所有在i时刻获赞的编号
        {
            cnt[x]++;
            if(cnt[x]>=k) st[x]=true;//如果长度一旦>=k,就说明曾经是热帖
        }
    }
    for(int i=0;i<N;i++)
    {
        if(st[i]) cout<<i<<endl;
    }
    return 0;
}

法2:

pair<int,int> logs记录 ,获赞时刻和id

将所有logs排个序,pair会按照先first再second的顺序排,即先按时刻排序,时刻相同再按编号排序。枚举所有的logs记录,i在前j在后,是两条记录,如果这两条记录的时间跨度>=d了,则cnt[logs[j].id]--,j往后走,直到两条记录的时间跨度<d。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n,d,k;
typedef pair<int, int> PII;
#define x first
#define y second
PII logs[N];//记录每条获赞记录的时刻和编号
int cnt[N];//记录每条记录的当前的获赞数量,动态变化,一旦时间跨度>=d就--
bool st[N];//记录是否是热帖

int main()
{
    cin>>n>>d>>k;
    for(int i=0;i<n;i++)
    {
        int ts,id;
        cin>>ts>>id;
        logs[i]={ts,id};
    }
    sort(logs,logs+n);
    for(int i=0,j=0;i<n;i++)//枚举每两条时间跨度<d的记录
    {
        while(logs[i].x-logs[j].x>=d)
        {
            cnt[logs[j].y]--;
            j++;
        }
        cnt[logs[i].y]++;
        if(cnt[logs[i].y]>=k)
        {
            st[logs[i].y]=true;
        }
    }
    for(int i=0;i<N;i++)
    {
        if(st[i]) cout<<i<<endl;
    }
    return 0;
}

2.献给阿尔吉侬的花束

板子题,直接看代码注释即可

/*
从起点S开始走,走到E结束,看看最短距离,由于有障碍物的阻隔,可能会走不到E点
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
const int N = 210;
char g[N][N];
int dist[N][N];//dist(i,j)表示(i,j)到起点的距离
int n,m;

int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int bfs(int sx,int sy,int ex,int ey)
{
    memset(dist,-1,sizeof dist);//dist为-1表示还没更新或者根本到不了
    dist[sx][sy]=0;
    queue<PII> q;
    q.push({sx,sy});
    
    while(q.size())
    {
        PII t=q.front();
        q.pop();
        
        for(int i=0;i<4;i++)
        {
            int x=t.x+dx[i],y=t.y+dy[i];
            if(x<0||x>=n||y<0||y>=m) continue;
            if(g[x][y]=='#') continue;
            if(dist[x][y]!=-1) continue;
            
            dist[x][y]=dist[t.x][t.y]+1;
            if(x==ex&y==ey) return dist[x][y];
            q.push({x,y});
        }
    }
    return -1;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        for(int i=0;i<n;i++) cin>>g[i];
        //找起点终点
        int sx,sy,ex,ey;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(g[i][j]=='S') sx=i,sy=j;
                if(g[i][j]=='E') ex=i,ey=j;
            }
        }
        int d=bfs(sx,sy,ex,ey);
        if(d==-1) cout<<"oop!"<<endl;
        else cout<<d<<endl;
    }
    return 0;
}

 3.红与黑

1.dfs代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 25;
int n,m;
char g[N][N];
bool st[N][N];

int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int dfs(int x,int y)
{
    int cnt=1;
    st[x][y]=true;
    for(int i=0;i<4;i++)
    {
        int a=x+dx[i],b=y+dy[i];
        if(a>=0 && a<m && b>=0 && b<n && g[a][b]=='.' && !st[a][b]) 
        {
            cnt+=dfs(a,b);
        }
    }
    return cnt;
}
int main()
{
    while (cin>>n>>m,m||n)
    {
        for(int i=0;i<m;i++) cin>>g[i];
    
        //找起点
        int sx,sy;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(g[i][j]=='@') 
                {
                    sx=i;
                    sy=j;
                    break;
                }
            }
        }
        memset(st, 0, sizeof st);//记得每次结束后更新st
        cout<<dfs(sx,sy)<<endl;
    }
    return 0;
}

2.bfs代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
#define x first
#define y second
const int N = 25;
int n,m;
char g[N][N];
bool st[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int bfs(int sx,int sy)
{
    int ans=1;
    queue<PII> q;
    q.push({sx,sy});
    st[sx][sy]=true;
    
    while(q.size())
    {
        PII t=q.front();
        
        q.pop();
        
        for(int i=0;i<4;i++)
        {
            int a=t.x+dx[i],b=t.y+dy[i];
            if(a<0||a>=m||b<0||b>=n) continue;
            if(g[a][b]=='#') continue;
            if(st[a][b]) continue;
            ans++;
            q.push({a,b});
            st[a][b]=true;
        }
    }
    return ans;
}
int main()
{
    while(cin>>n>>m,n||m)
    {
        for(int i=0;i<m;i++) cin>>g[i];
        int sx,sy;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(g[i][j]=='@')
                {
                    sx=i;
                    sy=j;
                    break;
                }
            }
        }
        cout<<bfs(sx,sy)<<endl;
        memset(st,0,sizeof st);
    }
    return 0;
}

4.交换瓶子

置换群法:

每个瓶子向它应该在的位置连一条边

如图所示 瓶子3应该在位置3,当前位置3放的是瓶子2,所以3和2之间连边

瓶子1应该在位置1,但现在位置1放的是瓶子3,所以1和3连边

瓶子2本应该放在位置2,但现在位置2放的是瓶子1,所以2和1连边

4,5同理

一共n个点,连n条边 

 当前图的性质 n个点,n条边,每个点出度入度都为1

这样的图必然是一堆环,称为一个置换

我们希望最终变成的是n个自环,每个点指向自己

初始环k个。终点环n个

情况1:交换同一个环内的点

环外的点没有任何变化

环内会裂开成俩环(这是因为两个点的出边改变了),环的数量+1

如图:交换1,3

情况2:交换不同环内的点 

结果会是将两个环合并

 综上:

我们每次操作都会是把两个环合并成1个或者是把一个环分裂

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010;
bool st[N];//找环
int b[N];
int n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>b[i];
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(!st[i])//在一个新的环里面
        {
            cnt++;
            for(int j=i;!st[j];j=b[j])//j指向的是j位置的这个瓶子b[j]
            {
                st[j]=true;
            }
        }
    }
    cout<<n-cnt<<endl;
    return 0;
}

成2个

初始k个环,最终n个环,每次操作最多增加一个环,所以最少n-k次操作

5.完全二叉树的权值

完全二叉树:
第i层的开头下标是2^(i-1)次方,结尾是2^i-1

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int a[N];
int Log[N];

int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    long long maxv=-1e18;
    int i=1,d=1;
    int ans=1;
    while(i<=n)
    {
        long long s=0;
        while(i <= ((1<<d)-1))
        {
            s+=a[i];
            i++;
        }
        if(s>maxv) 
        {
            maxv=s;
            ans=d;
        }
        d++;
    }
    cout<<ans<<endl;
    return 0;
}

6.地牢大师

和之前的题一样,只不过从2维变成了3维
结构体存坐标(x,y,z)
先找起点,终点,用bfs搜索路径

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 110;
char g[N][N][N];
int dist[N][N][N];//存储(x,y,z)到起点的最短距离
int dx[6]={1,-1,0,0,0,0};
int dy[6]={0,0,1,-1,0,0};
int dz[6]={0,0,0,0,1,-1};
struct Point{
    int x,y,z;
};
int l,r,c;

int bfs(Point start,Point end)
{
    memset(dist,-1,sizeof dist);//dist为-1说明还没更新,或者有阻挡到不了
    dist[start.x][start.y][start.z]=0;
    queue<Point> q;
    q.push(start);
    while(q.size())
    {
        Point t=q.front();
        q.pop();
        for(int i=0;i<6;i++)
        {
            int x=t.x+dx[i],y=t.y+dy[i],z=t.z+dz[i];
            if(x<0||x>=l||y<0||y>=r||z<0||z>=c) continue;
            if(g[x][y][z]=='#') continue;
            if(dist[x][y][z]!=-1) continue;
            dist[x][y][z]=dist[t.x][t.y][t.z]+1;
            if(x==end.x&&y==end.y&&z==end.z) return dist[x][y][z];
            q.push({x,y,z});
        }
    }
    return -1;
}
int main()
{
    Point start,end;
    while(cin>>l>>r>>c,l||r||c)
    {
        for(int i=0;i<l;i++)
        {
            for(int j=0;j<r;j++)
            {
                for(int k=0;k<c;k++)
                {
                    cin>>g[i][j][k];
                    if(g[i][j][k]=='S') start={i,j,k};
                    else if(g[i][j][k]=='E') end={i,j,k};
                }
            }
        }
        if(bfs(start,end)==-1) cout<<"Trapped!"<<endl;
        else cout<<"Escaped in "<<bfs(start,end)<<" minute(s)."<<endl;
    }
    return 0;
}

7.全球变暖 

用1个total来记录连通块中点的个数
用1个bound来记录被淹没的点的数量(一个点周围有'.'即被淹没)
如果bound==total则这个岛屿全部被淹没掉

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1010;
typedef pair<int, int> PII;
#define x first
#define y second
int n;
char g[N][N];
bool st[N][N];//此处st标记已经遍历过的陆地'#'
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

void bfs(int x,int y,int& total,int& bound)
{
    queue<PII> q;
    q.push({x,y});
    st[x][y]=true;
    
    while(q.size())
    {
        PII t=q.front();
        q.pop();
        total++;//记录连通块中点的个数
        bool flag=false;//表示这个岛屿是否被淹没
        
        for(int i=0;i<4;i++)
        {
            int x=t.x+dx[i],y=t.y+dy[i];
            if(x<0||x>=n||y<0||y>=n) continue;
            if(g[x][y]=='.')//周围这个点是大海,标记一下已沉没
            {
                flag=true;
                continue;
            }
            if(st[x][y]) continue;//如果周围这个点是陆地,且已经遍历过,就不用放在q中
            q.push({x,y});
            st[x][y]=true;
        }
        if(flag) bound++;
    }
}

int main()
{
    cin>>n;
    int res=0;
    for(int i=0;i<n;i++) cin>>g[i];
    
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(g[i][j]=='#'&&!st[i][j])
            {
                int total=0,bound=0;
                bfs(i,j,total,bound);
                //cout<<total<<" "<<bound<<endl;
                if(total==bound) res++;
            }
        }
    }
    cout<<res<<endl;
    return 0;
}

8.大臣的旅费

求树的直径:

方法:

1.任取一点x

2.找到距离x最远的点y

3.从y开始再进行一次搜索,找到距离y最远的点,此点距离y的距离就是直径

因为y是距离x最远的点,首先它一定是个端点

证明:y一定是树的直径上的端点

假设y不是树的直径的端点:

如下两种情况:

情况1:

xy这个路径不是树的直径但与树的直径uv有交点

因为y是距离x最远的点,所以边4+边1>边2+边1,所以边4>边1

所以边4+边3>边2+边3=uv=树的直径

所以存在>树的直径的边,显然不可能

情况2:

xy这个路径不是树的直径且与树的直径uv没有交点

y是距离x最远的点,所以边3+边5<边2

所以边5<边2  所以边5<边2+边3

所以边2+边3+边4>边5+边4=树的直径

所以存在>树的直径的边,显然不可能

因为y是直径一端的端点,所以第二遍距离y点最远的距离就是树的直径的距离

最终树的直径是dist的话花费就是

(1+10)+(2+10)+(3+10)+...+(dist+10)

=10*dist+(1+dist)*dist/2

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5+10,M = 2*N;
int h[N],e[M],ne[M],w[M],idx;
int n;
int dist[N];

void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int distance,int father)
{
    dist[u]=distance;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(j!=father)
        {
            dfs(j,distance+w[i],u);
        }
    }
}
int main()
{
    memset(h, -1, sizeof h);
    cin>>n;
    for(int i=0;i<n-1;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a, b, c);
        add(b ,a, c);
    }
    int u=1;
    dfs(u,0,-1);
    for(int i=2;i<=n;i++)
    {
        if(dist[i]>dist[u]) u=i;
    }
    dfs(u,0,-1);
    for(int i=1;i<=n;i++)
    {
        if(dist[i]>dist[u]) u=i;
    }
    printf("%lld",dist[u]*10+(LL)(dist[u]+1)*dist[u]/2);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值