牛客周赛 Round 54 解题

清楚姐姐的糖葫芦

代码

#include<iostream>

using namespace std;

string a;
int res;

int main(){
    cin>>a;
    
    for(int i=0;i<a.size();i++){
        if(a[i]=='o'){
            res++;
        }
    }
    
    cout<<res;
    
    return 0;
    
}

清楚姐姐买竹鼠

思路

我们就得分情况讨论了,看下三只一起买便宜还是单只买3只便宜,按照这个思想就很好写了。

#include<iostream>

#define int long long

using namespace std;

int a,b,x;

signed main(){
    cin>>a>>b>>x;
    
    int res;
    
    if(3*a>=b){
        if(x%3*a>=b){
            res=(x/3+1)*b;
        }else{
            res=(x/3)*b+x%3*a;
        }
    }else{
        res=a*x;
    }

    
    cout<<res;
    
    return 0;
}

竹鼠饲养物语

思路

我们可以通过观察规律可以知道:

  • 性质1:只要从 1 1 1 开始如果后面是连续的话,就可以算到答案内。
  • 性质2: 1 1 1 我们可以都要。

按照这些性质我们就可以用 map 来装所有数字出现的次数,如果当前的数值是之前数值+1,我们就把答案加上它的个数和上个个数的最小值(为什么呢?因为到当前级肯定是从上一级的基础上来的,也就是不能超过它的个数)

  • 细节:一旦不满足条件,我们立马退出循环,避免出现这种情况:
5
1 1 2 5 6
#include<iostream>
#include<vector>
#include<map>

#define int long long

using namespace std;

const int N = 1e5+10;

int w[N];
int n,m;
bool st[N];
int res;
map<int,int>S;

signed main(){
    cin>>n>>m;
    
    for(int i=1;i<=n;i++){
        cin>>w[i];
        S[w[i]]++;        
    }
    
    int last=0;
    int lastw=1e18;
    for(auto [v,w]:S){
        if(v==last+1){
            res+=min(lastw,w);
        }else{
            break;
        }
        
        last=v;
        lastw=min(lastw,w);
    }
    
    cout<<res;
    
    return 0;
    
}

清楚姐姐跳格子

思路

我们可以把这道题用图论的方式求最短路来做,因为是要每个因数,因此我们可以先建图(也就是先把每个位置上的因数处理来后连边),最后我们只需要跑个 dijistra 即可。

因为 n n n 很小,因此我们可以用差不多 n n n\sqrt n nn 的复杂度求出所有的因数并完成连边。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>

#define int long long
#define x first
#define y second

using namespace std;

const int N = 1e6+10,M = 2e7+10;

typedef pair<int,int>PII;

int e[M],ne[M],w[M],h[N],idx;
int a[N];
int n;
int dist[N];
bool st[N];

void add(int a,int b,int c){
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

void dijistra(){
    priority_queue<PII,vector<PII>,greater<PII>>q;
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    q.push({0,1});
    
    while(q.size()){
        int ver=q.top().y;
        q.pop();
        
        if(st[ver])continue;
        st[ver]=true;
        
        for(int i=h[ver];~i;i=ne[i]){
            int j=e[i];
            if(dist[j]>dist[ver]+w[i]){
                dist[j]=dist[ver]+w[i];
                q.push({dist[j],j});
            }
        }
    }
}

signed main(){
    cin>>n;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++){
        cin>>a[i];
        int b=max(abs(n-i),abs(i-1));
        for(int j=1;j<=b;j++){
            if(a[i]%j==0){
                if(i+j<=n){
                    add(i,i+j,1);
                }
                if(i-j>=1){
                    add(i,i-j,1);
                }
            }
        }
    }
    
    dijistra();
    
    cout<<dist[n];
    
    return 0;
    
}

清楚姐姐的布告规划

思路

这道题很明显的dp问题,因为存在选和不选然后求最优解的情况,因此我们可以设:
f[i][j]为已经处理好了前i个布告,此时的长度是j的最小方案数,那么状态转移就是:f[i][j]=min(f[i][j],f[k][j-w[k]]),1<=k<i

但对于这种选和不选的问题,我们往往可以优化掉一维,然后第二维度由大到小枚举。

  • 注意:在状态转移的过程中,布告不能够相互重叠,也就是我们的 j-w[i]>=ii>j
#include<iostream>
#include<algorithm>
#include<cstring>

#define int long long

using namespace std;

const int N = 2e6+10;

int f[N];//表示已经处理好了前i个布告,此时的长度是j的最小方案数
int n;
int T;
int w[N];

signed main(){
    cin>>T;
    
    while(T--){
        cin>>n;
        
        for(int i=1;i<=n;i++)cin>>w[i];
        
        memset(f,0x3f,sizeof f);
        
        f[0]=0;
        
        for(int i=1;i<=n;i++){
            for(int j=n;j>=w[i];j--){
                if(i>j||j-w[i]>=i)continue;
                f[j]=min(f[j],f[j-w[i]]+1);
            }
        }
        
        if(f[n]>n){
            f[n]=-1;
        }
        
        cout<<f[n]<<endl;
        
    }
    
    return 0;
}

竹鼠,宝藏与故事

方法(dfs O ( n × m ) O(n\times m) O(n×m)

dfs本身可以求出所有的路径,因此我们可以在dfs的过程中,把路径都存储下来,然后再检查一下路径是否合法已经是否被走过以及宝藏是否被访问过。

  • 注意:写dfs的时候最好写成匿名函数的形式,这样会大大提高运算速度。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<set>
#include<array>

#define int long long
#define x first
#define y second

using namespace std;

typedef pair<int,int>PII;

const int N = 2010;

int w[N][N];
int n,m;
int res;
bool st[N][N];
bool k[N][N];
int dx[]={-1,0,0,1},dy[]={0,-1,1,0};
queue<PII>q;
int chose[N][N];
bool vis[N][N];

signed main(){
    scanf("%lld%lld",&n,&m);
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%lld",&w[i][j]);
        }
    }
    
    chose[n][m]=1;//数组标记,到dfs回溯的时候记录路径
	//我们这边是用队列来存储所有的路径,chose是判断当前点是否要被选。
	//因为我们chose[n][m]=1,这样回溯就是从n,m开始的了。
    
    auto dfs=[&](auto dfs,int x,int y)->void{
        st[x][y]=true;
        for(int i=0;i<4;i++){
            int a=dx[i]+x,b=dy[i]+y;
            if(a<1||b<1||a>n||b>m||w[a][b]==-1||st[a][b])continue;
            dfs(dfs,a,b);
            if(chose[a][b]){
                chose[x][y]=1;
                q.push({x,y});
            }
        }
    };
    
    dfs(dfs,1,1);
    
    
    while(q.size()){
        PII t=q.front();
        q.pop();
        
        set<pair<int,int>>vis;
        
        int i=t.x,j=t.y;
        
        chose[i][j]=2;//就是表示被走过了,区分一下,chose=1是所有的路径
        
        auto check=[&](auto check,int x,int y,int sx,int sy)->void{
            vis.insert({x,y});
            
            for(int k=0;k<4;k++){
                int a=dx[k]+x,b=dy[k]+y;
                if(a==sx&&b==sy)continue;
                if(a==i&&b==j)continue;
                if(a<1||b<1||a>n||b>m||vis.count({a,b})||w[a][b]==-1)continue;
                if(!chose[a][b]){
                    check(check,a,b,x,y);
                }
                if(chose[a][b]&&chose[x][y]!=2){//如果点(a,b)可以到(x,y),那么这也是一条路径
                //也就是特判了这个:
                /*
				1 1 -1
				0 1 -1
				1 1 1
				就是1是我们的路径,因为回溯的时候没有算到这个的
				*/
                    chose[x][y]=1;
                    q.push({x,y});
                }
            }
        };
        check(check,i,j,-1,-1);//从每个路径上的点进行检查
    }
    
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(chose[i][j]!=0){
                res+=w[i][j];
            }
        }
    }
    
    if(!chose[1][1])res=0;
    
    printf("%lld",res);
    
    return 0;
    
}
  • 17
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值