牛客周赛48补题DEF周赛47F

周赛48D小红的乘2除2
 

题目描述

小红拿到了一个数组。她需要进行恰好一次以下操作:
先使得一个元素除以2,向下取整。然后使得一个元素乘以2。
小红希望操作后,数组的陡峭值尽可能小。你能帮帮她吗?
定义数组的陡峭值为:相邻两数之差的绝对值之和。例如,[2,3,1]的陡峭值为|2-3|+|3-1|=3。

输入描述:

第一行输入一个正整数nnn,代表数组大小。
第二行输入nnn个正整数aia_iai​,代表小红拿到的数组。
2≤n≤1052 \leq n \leq 10^52≤n≤105
1≤ai≤1091 \leq a_i \leq 10^91≤ai​≤109

输出描述:

一个整数,代表最终数组的陡峭值的最小值。

示例1

输入

复制3 2 3 2

3
2 3 2

输出

复制0

0

说明

先对第二个元素除以2,然后对第二个元素乘以2即可。

示例2

输入

复制4 1 2 3 4

4
1 2 3 4

输出

复制2

2

说明

先对第四个元素除以2,然后对第一个元素乘以2。

做法:

分类讨论,i和j的位置有三种情况。一是i=j,即除以2和乘以2是同一个数。二是i+1=j,即除以二的数和乘以二的数相邻。三是i和j的距离大于1,这时i和j互不影响。

#include<bits/stdc++.h>
using namespace std;
int n;
long long res;//原始陡峭值 
long long ans=0x3f3f3f3f3f3f3f3f,ans2=0x3f3f3f3f3f3f3f3f;
long long a[100010];
int main(){
    vector<int> v;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        if(i!=1)res+=fabs(a[i]-a[i-1]);
    }
	 
    //特判n=2的情况 
    if(n==2){
        long long cnt=fabs(a[1]/2*2-a[2]),cnt2=fabs(a[1]-a[2]/2*2);
        cnt=min(cnt,cnt2);
        cnt2=fabs(a[1]/2-a[2]*2);
         cnt=min(cnt,cnt2);
        cnt2=fabs(a[1]*2-a[2]/2);
         cnt=min(cnt,cnt2);
        cout<<cnt;
        return 0;
    }
    
    //找出除以二后陡峭值最小的坐标 
    int idex;
    for(int i=1;i<=n;i++){
        long long cnt;
        if(i==1){
            cnt=res-fabs(a[i+1]-a[i])+fabs(a[i+1]-a[i]/2);
            if(ans>cnt){
                ans=cnt;
                idex=i;
            }
        }
        else if(i==n){
            cnt=res-fabs(a[i]-a[i-1])+fabs(a[i]/2-a[i-1]);
            if(ans>cnt){
                ans=cnt;
                idex=i;
            }
        }
        else{
            cnt=res-fabs(a[i]-a[i-1])-fabs(a[i+1]-a[i])+fabs(a[i+1]-a[i]/2)+fabs(a[i]/2-a[i-1]);
            if(ans>cnt){
                ans=cnt;
                idex=i;
            }
        }
    }
    //看是否有多个数除以二后陡峭值最小,下标放入vector 
    for(int i=1;i<=n;i++){
        long long cnt;
        if(i==1){
            cnt=res-fabs(a[i+1]-a[i])+fabs(a[i+1]-a[i]/2);
            if(ans==cnt){
                v.push_back(i);
            }
        }
        else if(i==n){
            cnt=res-fabs(a[i]-a[i-1])+fabs(a[i]/2-a[i-1]);
            if(ans==cnt){
                v.push_back(i);
            }
        }
        else{
            cnt=res-fabs(a[i]-a[i-1])-fabs(a[i+1]-a[i])+fabs(a[i+1]-a[i]/2)+fabs(a[i]/2-a[i-1]);
            if(ans==cnt){
                v.push_back(i);
            }
        }
    }
    //找出乘以2后陡峭值最小的 (因为两个数互不影响,所以可以分开求) 
    for(int j=0;j<v.size();j++){
        if(j==v[j]||j==v[j]+1||j==v[j]-1) continue;//除去其他两种情况 
        int tmp=a[v[j]];
        a[v[j]]/=2;
    	for(int i=1;i<=n;i++){
        	long long cnt;
        	if(i==1){
	            cnt=ans-fabs(a[i+1]-a[i])+fabs(a[i+1]-a[i]*2);
    	        if(ans2>cnt){
        	        ans2=cnt;
            	}
        	}
	        else if(i==n){
    	        cnt=ans-fabs(a[i]-a[i-1])+fabs(a[i]*2-a[i-1]);
        	    if(ans2>cnt){
            	    ans2=cnt;
            	}
        	}
        	else{
            	cnt=ans-fabs(a[i]-a[i-1])-fabs(a[i+1]-a[i])+fabs(a[i+1]-a[i]*2)+fabs(a[i]*2-a[i-1]);
            	if(ans2>cnt){
                	ans2=cnt;
            	}
        	}
    	}
    	a[v[j]]=tmp;
	}
    
    //除以二,乘以二为同一个数 
    for(int i=1;i<=n;i++){
        long long cnt;
        if(i==1){
            cnt=res+fabs(a[i]/2*2-a[i+1])-fabs(a[i]-a[i+1]);
        }
        else if(i==n){
            cnt=res+fabs(a[i-1]-a[i]/2*2)-fabs(a[i-1]-a[i]);
        }
        else {
            cnt=res+fabs(a[i]/2*2-a[i+1])-fabs(a[i]-a[i+1])+fabs(a[i-1]-a[i]/2*2)-fabs(a[i-1]-a[i]);
        }
        ans2=min(ans2,cnt);
    }
    
    //除以二的数和乘以二的数相邻 
    for(int i=1;i<n;i++){
        long long cnt1,cnt2;
        if(i==1){
            cnt1=res+fabs(a[i]/2-a[i+1]*2)-fabs(a[i]-a[i+1])+fabs(a[i+1]*2-a[i+2])-fabs(a[i+1]-a[i+2]);
            cnt2=res+fabs(a[i]*2-a[i+1]/2)-fabs(a[i]-a[i+1])+fabs(a[i+1]/2-a[i+2])-fabs(a[i+1]-a[i+2]);
        }
        else if(i==n-1){
            cnt1=res+fabs(a[i-1]-a[i]/2)-fabs(a[i-1]-a[i])+fabs(a[i]/2-a[i+1]*2)-fabs(a[i]-a[i+1]);
            cnt2=res+fabs(a[i-1]-a[i]*2)-fabs(a[i-1]-a[i])+fabs(a[i]*2-a[i+1]/2)-fabs(a[i]-a[i+1]);
        }
        else{
            cnt1=res+fabs(a[i-1]-a[i]/2)-fabs(a[i-1]-a[i])+fabs(a[i]/2-a[i+1]*2)-fabs(a[i]-a[i+1])+fabs(a[i+1]*2-a[i+2])-fabs(a[i+1]-a[i+2]);
            cnt2=res+fabs(a[i-1]-a[i]*2)-fabs(a[i-1]-a[i])+fabs(a[i]*2-a[i+1]/2)-fabs(a[i]-a[i+1])+fabs(a[i+1]/2-a[i+2])-fabs(a[i+1]-a[i+2]);
        }
        ans2=min(cnt1,ans2);
        ans2=min(cnt2,ans2);
    }
    
    cout<<ans2;
}

WA的原因

我只考虑到乘以2的数和除以2 的数互不影响的情况,没想到其他两种情况,就是没想到这三种情况是不一样的。我只想着先找到除以二使得陡峭值最小的数,再找乘以二使得陡峭值最小的数就行了,没想到他们的位置关系也是会有影响的,会多减多加一些数。

周赛48F小红的迷宫行走

题目描述

小红来到了一个矩形迷宫,每个房间写着一个数字。小红初始在左上角的房间,她准备前往该迷宫的右下角房间,每次小红可以向右或者向下行走一步。
另外,小红可以进行若干次传送,每次可以花费1的步数,前往和当前房间不互素的任意一个房间。
现在,小红希望你求出从左上角走到右下角的最小步数。你能帮帮她吗?

输入描述:

第一行输入两个正整数n,mn,mn,m,代表迷宫的行数和列数。
接下来的nnn行,每行输入mmm个正整数aija_{ij}aij​,用来表示每个房间的数字。
1≤n,m≤5001\leq n,m \leq 5001≤n,m≤500
1≤aij≤1051\leq a_{ij} \leq 10^51≤aij​≤105

输出描述:

一个整数,代表左上角走到右下角的最小步数。

示例1

输入

复制3 3 1 2 3 2 3 3 3 4 5

3 3
1 2 3
2 3 3
3 4 5

输出

复制3

3

做法

最短路加边,大约有2e5个点,每个点都建边的话肯定会爆。不互素就可以传送,那就是两个数有一个相同的质因子就可以传送,那么我们只需要找出地图中每个数的最小质因子,根据每个最小质因子搭个平台,中转一下。上去平台需要花费1代价,从下来到达某点花费0代价,从平台可以走向任何一个最小质因数和该平台相同的数。该过程既是传送。然后跑一遍最短路即可。

#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[510][510];
struct ty
{
    int id, w;
};
struct ty2
{
    int id, dis;
    bool operator<(const ty2 &a) const
    {
        return dis > a.dis;
    }
};
vector<ty> v[400010];
int p[100010];
int dis[400010], vis[400010];
priority_queue<ty2> q;
void dij()
{
    q.push({0, 0});
    dis[0] = 0;
    while (!q.empty())
    {
        ty2 tmp = q.top();
        q.pop();
        if (vis[tmp.id])
            continue;
        vis[tmp.id] = 1;
        for (int i = 0; i < v[tmp.id].size(); i++)
        {
            if (vis[v[tmp.id][i].id])
                continue;
            if (dis[tmp.id] + v[tmp.id][i].w < dis[v[tmp.id][i].id])
            {
                dis[v[tmp.id][i].id] = dis[tmp.id] + v[tmp.id][i].w;
                q.push({v[tmp.id][i].id, dis[v[tmp.id][i].id]});
            }
        }
    }
}
int main()
{
    for (int i = 2; i <= 100000; i++)//找出最小质因子
    {
        if (p[i])
            continue;
        for (int j = i; j <= 100000; j += i)
        {
            if (!p[j])
                p[j] = i;
        }
    }
    cin >> n >> m;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            scanf("%d", &a[i][j]);
        }
    }
    for (int i = 0; i < n; i++)//建边
    {
        for (int j = 0; j < m; j++)
        {
            if (i != n - 1)
            { // 向下
                v[i * m + j].push_back({(i + 1) * m + j, 1});
            }
            if (j != m - 1)
            { // 向右
                v[i * m + j].push_back({i * m + j + 1, 1});
            }
            if (p[a[i][j]] != 0){//平台
                v[i * m + j].push_back({300000 + p[a[i][j]], 1});
                v[300000 + p[a[i][j]]].push_back({i * m + j, 0});
            }
        }
    }
    memset(dis, 0x3f, sizeof(dis));
    dij();
    if (dis[(n - 1) * m + m - 1] == 0x3f3f3f3f)
        cout << -1;
    else
        cout << dis[(n - 1) * m + m - 1];
}

周赛47F花花的地图

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

A市地图是一个n×mn \times mn×m的网格图,字符'#'表示障碍,'.' 表示空地。花花住在左上角(1,1)(1,1)(1,1),花花的朋友萌萌住在右下角(n,m)(n,m)(n,m)。花花要去萌萌家玩。
花花如果走到空地则不需要任何代价,但是如果要走到障碍点则他需要先摧毁该障碍。每次可以走到相邻的四个方向之一,即从(x,y)(x,y)(x,y)可以走到(x+1,y)(x+1,y)(x+1,y),(x−1,y)(x-1,y)(x−1,y),(x,y+1)(x,y+1)(x,y+1),(x,y−1)(x,y-1)(x,y−1)。
花花可以花费CiC_iCi​的代价将第iii列的所有障碍都消灭。
请帮花花计算她去萌萌家最少需要多少代价。

输入描述:

第一行输入两个正整数nnn,mmm,为网格图的大小。
接下来nnn 行,每行mmm 个字符,描述迷宫的地图。字符仅由'.','#',保证起点一定是空地。
接下来一行mmm个正整数CiC_iCi​,为消灭第iii列的障碍的代价
1≤n,m≤1001 \leq n,m \leq 1001≤n,m≤100
1≤Ci≤1051 \leq C_i \leq 10^51≤Ci​≤105

输出描述:

输出一个整数,为从花花家到萌萌家的最小代价。

示例1

输入

复制4 4 .##. .#.# .### ..#. 5 3 9 4

4 4
.##.
.#.#
.###
..#.
5 3 9 4

输出

复制7

7

说明

消除第2列和第4列的障碍,总代价为7

做法

给每一列搭一个平台,从相邻的列到平台需要c[j]的代价,从平台到该列的任意一行代价为0。

这题和上题对平台的处理有点不一样,上一题是直接将每个点建一条边到平台上,然后直接跑dij。这题没有建边,直接把符合的点塞进优先队列里,算是从平台上走下来。

#include<bits/stdc++.h>
using namespace std;
int n,m;
char a[110][110];
int dis[20000];
int vis[20000];
int c[110];
struct ty{
    int id,w;  
};
struct ty2{
    int id,dis;
    bool operator < (const ty2 &a) const{
        return dis>a.dis;      
    }
};
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
priority_queue<ty2> q;
vector<ty> v[20000];
void dij(){
    q.push({0,0});
    dis[0]=0;
    while(!q.empty()){
        ty2 tmp=q.top();
        q.pop();
        if(vis[tmp.id]) continue;
        vis[tmp.id]=1;
        int x=tmp.id/m,y=tmp.id%m;
        for(int i=0;i<4;i++){
            int xx=x+dx[i],yy=y+dy[i];
            if(xx<0||xx>=n||yy<0||yy>=m) continue;
            if(vis[xx*m+yy]) continue;
            if(a[xx][yy]=='#'){//走向平台
                for(int j=0;j<n;j++){//平台走向该列的任意一点
                    if(vis[j*m+yy]) continue;
                    if(dis[tmp.id]+c[yy]<dis[j*m+yy]){
                        dis[j*m+yy]=dis[tmp.id]+c[yy];
                        q.push({j*m+yy,dis[tmp.id]+c[yy]});
                    }
                }
            }
            else{
                if(dis[tmp.id]<dis[xx*m+yy]){
                    dis[xx*m+yy]=dis[tmp.id];
                    q.push({xx*m+yy,dis[tmp.id]});
                }
            }
        }
    }
}
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>a[i][j];
        }
    }
    for(int i=0;i<m;i++) scanf("%d",&c[i]);
    memset(dis,0x3f,sizeof(dis));
    dij();
    cout<<dis[(n-1)*m+m-1];
}

wa的原因

1.段错误(数组开小了,范围算错了qaq)

2.没有想到要搭平台,从该列到别的列直接分类讨论不同的代价然后直接建边了,后来看视频发现这样做不行,还是要搭平台。因为位于同一列遇到#就不知道到底要不要加上代价c[j]。

周赛48E小红的伪回文子串(hard)

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

本题和easy版的唯一区别是数据范围不同!

定义一个字符串的“伪回文值”是:修改最少字符数量使得其变成回文串的修改次数。例如,"abca"的伪回文值是1。任何回文串的伪回文值是0。

现在小红拿到了一个字符串,她希望你求出所有连续子串的伪回文值之和。你能帮帮她吗?

输入描述:

一个仅包含小写字母的字符串。长度不超过200000200000200000

输出描述:

所有连续子串的回文值之和。

示例1

输入

复制abca

abca

输出

复制6

6

说明

所有长度不小于2的子串的伪回文值都是1。其余子串伪回文值是0。

示例2

输入

复制acac

acac

输出

复制5

5

做法

easy版本的用n的3次方也能过

#include<bits/stdc++.h>
using namespace std;
string s;
long long ans;
int main(){
    cin>>s;
    for(int i=0;i<s.size();i++){
        for(int j=1;j+i<=s.size();j++){
            string a=s.substr(i,j);
            for(int k=0;k<a.size()/2;k++){
                if(a[k]!=a[a.size()-1-k]) ans++;
            }
        }
    }
    cout<<ans;
}

有一个n的2次方的做法,就是枚举i和j,看s[i]和s[j]这对字母对子串的贡献,但还是过不了hard版

#include<bits/stdc++.h>
using namespace std;
string s;
long long ans;
int main(){
    cin>>s;
    int n=s.size();
    for(int i=0;i<s.size();i++){
        for(int j=i+1;j<s.size();j++){
            if(s[i]!=s[j]){
                ans+=min(i+1,n-j);
            }
        }
    }
    cout<<ans;
}

O(n)做法是只枚举i,然后可以发现其中的规律。

#include<bits/stdc++.h>
using namespace std;
string s;
long long ans;
long long qzh[26][200010],sum[26][200010];
int main(){
    cin>>s;
    s='#'+s;
    int n=s.size()-1;
    for(int i=1;i<=n;i++){
        for(int j=0;j<26;j++){
        	if(s[i]-'a'!=j) qzh[j][i]=qzh[j][i-1]+1;
        	else qzh[j][i]=qzh[j][i-1];
        	if(s[i]-'a'!=j) sum[j][i]=sum[j][i-1]+n+1-i;
            else sum[j][i]=sum[j][i-1];
        }
    }
    for(int i=1;i<n;i++){
    	if(i<(n+1)/2)
    	    ans+=i*(qzh[s[i]-'a'][n+1-i]-qzh[s[i]-'a'][i])+sum[s[i]-'a'][n]-sum[s[i]-'a'][n+1-i];
    	else
    	    ans+=sum[s[i]-'a'][n]-sum[s[i]-'a'][i];
    }
    cout<<ans;
}

wa的原因

没开long long(qzh数组不用long long,但sum数组要用,等差数列1到200000会爆)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值