牛客小白赛89题解

这场难度真大了好多QWQ

A.贪心,只要首尾其中一个位置是空的就代表可以平移(即相似)。

#include<bits/stdc++.h>
using namespace std;
int n,m,a[1000010];
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    sort(a,a+n);
    if(a[0]!=1||a[n-1]!=m){
        cout<<"Yes";
    }else cout<<"No";
    return 0;
}

B.贪心+前缀和思想,先从小到大排序,对负数进行累加并求和,再让每个正数加上这部分后求和。具体见注释。

#include<bits/stdc++.h>
using namespace std;
#define int long long//记得开long long
int t,a[1000010],b[1000010];
signed main(){
    cin>>t;
    while(t--){
        int n,sum=0,ans=0;//sum代表总的累加,ans维护到当前元素时负数的累加值
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++){
            if(a[i]<0){
                ans+=a[i-1];//累加
                sum+=(a[i]+ans)*(n-i-1);//此时a[i]+ans即为累加后的a[i]真实值,将后面的(n-i-1)元素都加上当前值。
            }else {
                sum+=a[i]+ans;//将正数加上负数的累加值后求和
            }
        }
        cout<<sum<<'\n';
    }
    return 0;
}

C.bfs+博弈论。只有当场上全是蓝色区域是蓝方才能赢。否则,如果红方能通过一次操作将所有蓝色区域染红则红方赢,否则平局。(bfs的具体操作不做赘述)

#include<bits/stdc++.h>
using namespace std;
#define int long long
int t,book[2010][2010],ans,cnt;//cnt记录红色联通块的数量,值不同即为不同的连通块
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};//两个方向数组
char s[2010][2010];
signed main(){
    cin>>t;
    while(t--){
        int n,m,cc=0;//cc为蓝色格子的数量
        cin>>n>>m;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                book[i][j]=0;
                cin>>s[i][j];
                if(s[i][j]=='.')cc++;
            }
        }
        ans=0;
        if(cc==n*m){
            cout<<"Blue\n";
        }else {
            for(int i=0;i<n;i++){
                for(int j=0;j<m;j++){
                    if(ans)break;//如果已经找到能将蓝色染完的红色联通块就不用再遍历,直接跳出
                   if(s[i][j]=='#'&&book[i][j]==0){
                       int cns=0;//cns记录当前红色联通块的染色数量
                       map<pair<int,int>,int>kk;//标记蓝色方块,避免重复染色
                       queue<pair<int,int>>q;
                       q.push({i,j});
                       book[i][j]=++cnt;
                       while(!q.empty()){
                           int x=q.front().first;
                           int y=q.front().second;
                           q.pop();
                           for(int ii=0;ii<4;ii++){
                               int newx=x+fx[ii];
                               int newy=y+fy[ii];
                               if(newx>=0&&newx<n&&newy>=0&&newy<m&&s[newx][newy]=='.'&&kk[{newx,newy}]==0){
                                   kk[{newx,newy}]=1;
                                   cns++;
                               }else if(newx>=0&&newx<n&&newy>=0&&newy<m&&s[newx][newy]=='#'&&book[newx][newy]==0){
                                   book[newx][newy]=cnt;
                                   q.push({newx,newy});
                               }
                           }
                       }
                       if(cns==cc){//如果全染完就跳出
                           ans=1;
                           break;
                       }
                   }
                }
            }
            if(ans){
                cout<<"Red\n";
            }else {
                cout<<"Draw\n";
            }
        }
    }
    return 0;
}

D.单源最短路(dij)+dp.具体见注释(迪杰斯特拉算法不再赘述,纯板子)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7,N=1e6+10;
int n,m,q,x,dp[5010][5010],bk[5010],dis[1000010],book[1000010];
vector<int>V[N];
void dij(){
    for(int i=1;i<=n;i++){
        dis[i]=1e18;
        book[i]=0;
    }
    dis[x]=0;
    queue<pair<int,int>>q;
    q.push({0,x});
    while(!q.empty()){
        int u=q.front().second;
        q.pop();
        for(int i=0;i<V[u].size();i++){
            int v=V[u][i];
            if(dis[v]>dis[u]+1){
                dis[v]=dis[u]+1;
                q.push({dis[v],v});
            }
        }
    }
}
signed main(){
    cin>>n>>m>>q>>x;
    for(int i=0;i<m;i++){
        int u,v;
        cin>>u>>v;
        V[u].push_back(v);
        V[v].push_back(u);
    }
    dij();
    for(int i=1;i<=n;i++){
        bk[dis[i]]++;
    }
    for(int i=1;i<=5000;i++){//dp[i][j]表示到距离i为止,选了j个点的方案数
        dp[i][1]=dp[i-1][1];//不选
        dp[i][1]+=bk[i];//选,加上当前距离结点数
        dp[i][1]%=mod;
        for(int j=2;j<=i;j++){
            dp[i][j]=dp[i-1][j];//不选的状态转移
            dp[i][j]+=dp[i-1][j-1]*bk[i];//选的状态转移,记得乘上结点数
            dp[i][j]%=mod;//取模
        }
        
    }
    while(q--){
        int k;
        cin>>k;
        cout<<dp[5000][k]<<'\n';
    }
    return 0;
}

E.dp。要使路径只有一条,则相邻列的连续格子必须在横向方向上只重合一格。

设a[i]表示第i列的连续格子长度

如果a[i]不为1,这只有下图两种情况(画的丑见谅QWQ)

当a[i]为1时,无论怎样都只有一个格子重合,不用考虑长度是否越界,发现与差分性质相同,于是利用差分数组维护这种情况,具体见注释。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e3+5;
int n,m,a[N],dp[N][N],cf[N];//cf为差分数组
void solve(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>a[i];
    }
    //dp[i][j]表示到第i列时,以j位置为开头的合法连续串是否存在,值为1就是存在,为0就是不存在
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            dp[i][j]=0;//初始化为0
        }
    }
    for(int i=1;i<=n;i++){
        dp[1][i]=1;//将第一列单独处理下
    }
    for(int i=2;i<=m;i++){
        if(a[i]==1){//值为1的情况
            for(int j=1;j<=n;j++){
                cf[j]=0;//初始化差分数组
            }
            for(int j=1;j+a[i-1]-1<=n;j++){
                if(dp[i-1][j]){//差分操作
                    cf[j]++;
                    cf[j+a[i-1]]--;
                }
            }
            for(int j=1;j<=n;j++){
                cf[j]+=cf[j-1];//将差分累加得到当前位置的长度为1的串存在信息,1表示存在,0表示不存在
                if(cf[j]){
                    dp[i][j]=1;//转换到dp数组中去
                }
            }
        }else {
            for(int j=1;j+a[i]-1<=n;j++){
                dp[i][j]|=dp[i-1][j+a[i]-1];//头部与上一列重合
                if(j-a[i-1]+1>=1)dp[i][j]|=dp[i-1][j-a[i-1]+1];//尾部与上一列重合
                //由于只要知道每个位置存在与否,我这里直接用了或运算,实际上加也可以
            }
        }
    }
    int ans=0;//第m列的合法位置数
    for(int i=1;i<=n;i++){
        if(dp[m][i])ans++;
    }
    cout<<ans<<'\n';
}
signed main(){
    int t=1;
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

F.bfs

#include<bits/stdc++.h>
using namespace std;
int n,m,bk[2000010];//bk[i]表示第i个结点最先出现生物的时间,只要出现过就代表此后的任意时间点该位置都有可能存在生物
vector<int>V[2000010];//存图
vector<int>book[2000010];//book[i]存储了所有i时间出生的生物的出生结点
signed main(){
    cin>>n>>m;
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        V[u].push_back(v);
        V[v].push_back(u);
    }
    for(int i=1;i<=m;i++){
        int aa,tt;
        cin>>aa>>tt;
        book[tt].push_back(aa);
    }
    for(int i=0;i<=n;i++)bk[i]=-1;//初始化为-1,因为时间从0开始
    queue<pair<int,int>>q,p;//定义两个队列交替操作,同一队列中的结点全部为同一时间点有生物出现在该结点,pair第一部分存结点,第二部分存父结点
    for(int i=0;i<=1e8;i++){//开大点,出生时间到1e6不代表遍历时间只到1e6,中途会跳出,不会超时
        if(i&1){
            for(int j=0;j<book[i].size();j++){//遍历当前时间出生的所有生物
                if(bk[book[i][j]]!=-1){//如果该结点被访问过,表示此时该结点可能有生物,可能碰面,输出并跳出
                    cout<<i*2;
                    return 0;
                }else {//否则说明该结点此前没有可能出现生物,将该节点压入另一队列,并标记。
                    bk[book[i][j]]=i;
                    p.push({book[i][j],-1});
                }
            }
            while(!p.empty()){//bfs,将队列中的结点
                int xx=p.front().first;
                int yy=p.front().second;
                p.pop();
                for(int j=0;j<V[xx].size();j++){
                    if(yy==V[xx][j])continue;
                    if(bk[V[xx][j]]!=-1){
                        if(bk[V[xx][j]]==i){
                            cout<<i*2+1;
                        }else {
                            cout<<(i+1)*2;
                        }
                        return 0;
                    }else {
                        bk[V[xx][j]]=i+1;
                        q.push({V[xx][j],xx});
                    }
                }
            }
        }else {//与上半部分相同,只改变p,q;
            for(int j=0;j<book[i].size();j++){
                if(bk[book[i][j]]!=-1){
                    cout<<i*2;
                    return 0;
                }else {
                    bk[book[i][j]]=i;
                    q.push({book[i][j],-1});
                }
            }
            while(!q.empty()){
                int xx=q.front().first;
                int yy=q.front().second;
                q.pop();
                for(int j=0;j<V[xx].size();j++){
                    if(yy==V[xx][j])continue;
                    if(bk[V[xx][j]]!=-1){
                        if(bk[V[xx][j]]==i){
                            cout<<i*2+1;
                        }else {
                            cout<<(i+1)*2;
                        }
                        return 0;
                    }else {
                        bk[V[xx][j]]=i+1;
                        p.push({V[xx][j],xx});
                    }
                }
            }
        }
    }
    return 0;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值