Codeforces Round #739 (Div. 3)详解

Codeforces Round #739 (Div. 3)

https://codeforces.com/contest/1560

A Dislike of Threes(💧水题)

去掉所有数中个位含有3的还有能被三整除的,放入数组中,输出给定顺序的某个位置的数字

#include<iostream>
#include<algorithm>
#include<queue>
typedef long long LL;
using namespace std;
const int N=3000;
int a[N],cnt=1;
bool check(int n)
{
    if (n%10==3)  return true;
    return false;
    
}
void init(int n){
    for(int i=1;i<=n;i++)
    {
        if(i%3==0||check(i)) continue;
        //cout<<i<<" "<<cnt<<endl;
        a[cnt++]=i;
    }
}
int main()
{
    init(3000);
    int t;
    cin>>t;
    while (t--)
    {
        int k;
        cin>>k;        
        cout<<a[k]<<endl;
    }
    
    return 0;
}

B Who’s Opposite?(找规律)

#include<iostream>
#include<cmath>
using namespace std;
int main()
{
    int t;
    cin>>t;
    while (t--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        int d=abs(a-b);
        if(a>d*2||b>d*2||c>d*2){
            puts("-1");
            continue;
        }
        if(c<=d) cout<<c+d<<endl;
        else if(c>d) cout<<c-d<<endl;
    } 
}

C Infinity Table(矩阵填数(找规律))

img
题意:

给出一个数字T,输出它所在的位置坐标(i,j)

思路:

  1. 每行的第一个数字等于它所在的行数的平方( 1 2 , 2 2 , 3 2 ⋅ ⋅ ⋅ ⋅ 1^2,2^2,3^2···· 12,22,32)

  2. 以图示形式填数,每一部分的数字范围为 ( i − 1 ) 2 ~ i 2 (i-1)^2~i^2 (i1)2i2

思路1:将每一行的第一个数字存入数组中(从1开始存)

思路2:二分查找找到数字所在的部分的左右边界l,r

二分查找数组中小于等于数字T数字的下标为l,这就是这个数字所在的“部分”的左边界,右边界怎么求?右边界r等于上一行的第一个数字+1,所以让r=l-1(上一行第一个数的下标,在数组中l的前一个位置)

  1. 观察任意一部分,一定有奇数个数字,中间位置的数字行数=列数=i( ( 1 , 1 ) , ( 2 , 2 ) , ( 3 , 3 ) ⋅ ⋅ ⋅ ⋅ (1,1),(2,2),(3,3)···· (1,1),(2,2),(3,3)​),对于每一部分中的任意一个数x ( i − 1 ) 2 − 1 < x < i 2 (i-1)^2-1<x<i^2 (i1)21<x<i2,中间数字等于每一部分两端数字除2(中位数),比如第四部分的中位数为13=(10+16)/2,下标为(4,4)

思路3:找到这一部分的中间数字mid=(f[l]+f[r]+1)>>1(中位数)

  1. 如果数字x<mid,列数不变,行数不断-1,所以 i x = i m i d − ( m i d − x ) i_{x}=i_{mid}-(mid-x) ix=imid(midx)​​​

    如果数字x>mid,行数不变,列数不断-1,所以 j x = j m i d − ( x − m i d ) j_x=j_{mid}-(x-mid) jx=jmid(xmid)​​

思路4:利用数字x与这一部分中位数的差值计算(i,j)

代码:
#include <bits/stdc++.h>
using namespace std;

int t, x;
vector<int> f;
void init()
{
	f.push_back(0);
    for (int i = 1; i < 0x3f3f3f3f / i; i++)
        f.push_back(i * i);
}

int main()
{
    init();
    
    scanf("%d", &t);
    while (t --)
    {
        scanf("%d", &x);
        int r = lower_bound(f.begin(), f.end(), x) - f.begin(), l = r - 1;
        int mid = (f[l] + f[r] + 1) >> 1, a = r, b = r;
        
        if (mid > x) a -= mid - x;
        else b -= x - mid;
        
        printf("%d %d\n", a, b);
    }

    return 0;
}

D Make a Power of Two(思维+贪心)

题意:

给出一个数字,只能进行两种操作:

  1. 删除任意一个数字
  2. 从右边添加一个数字

最少进行多少种操作,让给出的数字变成2的N次方?

思路:

  1. 如果数字本来就是2的N次方,操作0次

    (二进制法判断是否是2的n次方)

  2. 核心思路:打表存储 2 n 2^n 2n,对于每一个数,for循环枚举数组中的 2 n 2^n 2n,计算变成这些数字中最小需要几步

  3. 如何计算至少进行几步操作?删除的操作数+添加的操作数

    1. 寻找最大相同连续子串数目(双指针)

    2. 操作数=a.length()-cnt+b.length()-cnt

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
string str[70];
bool check(int x)//判断是否是2的n次方
{
    return x&(x-1)==0;
}
void init()	//打表2^i
{
    for(int i=0;i<=64;i++)
    {
        ll x =1ll*1<<i;//“*1ll”是因为1为int类型,防止等号后面爆int
        str[i]=to_string(x);
    }
}
int count(string a,string b)//双指针寻找最大相同连续子串数目,计算操作数目
{
    int l1=a.length();
    int l2=b.length();
    int cnt=0;
    for(int i=0,j=0;i<a.length();i++)
    {
        if(a[i]==b[j])
        {
            cnt++;
            j++;
        }        
    }
    return l1-cnt+l2-cnt;
}
int main()
{
    init();
    int t;
    cin>>t;
    while (t--)
    {
        int n;
        cin>>n;
        if(check(n))
        {
            puts("0");
            continue;
        }else{
            int ans=INF;
            for(int i=0;i<=64;i++)//找出把给定数字变成2的i次方中最小操作数目
            {
                ans=min(ans,count(to_string(n),str[i]));
            }
            cout<<ans<<endl;
        }
        
    }
    
    
    return 0;
}

E Polycarp and String Transformation(字符串拼接)

题意:

一个字符串t,只能进行两种操作:

  1. 删除一种这个字符串中出现的字母,删除后的字符串为s
  2. 拼接t=t+s

直到字符串s被删空,所有的字符都被删除。

题目会给定一个已经操作完毕后的字符串t,要求输出它的原字符串,并且输出一个按照删除顺序形成的字符串

思路:

(1).我们会发现先删除的字符串在结果串t中,最终出现的位置会更靠前,越往后删除的元素在结果串中的最终出现的位置越靠后。

所以我们从后往前遍历所有元素,从来没有出现过的元素一定是最后删除的元素, 放入数组中(所以数组是按删除顺序的倒序排列的)

(2).不同元素的个数就是删除的次数

(3).如果一个字符串一共进行了3次删减,串s就空了

(4)删除的越早的元素出现的次数越少,原始字符的个数=结果串中出现的次数/(元素个数-删除数组下标)

(5).所有元素原始个数相加就是初始字符串的长度len,截取给定字符串的前len长度即为初始字符串string ss=s.sub_str(0,len)

(6).eg:结果串abacabaaacaac

删除顺序为cab,在数组中顺序为bac,下标为0,1,2

a在结果串中出现的次数为8,删除次序为2,下标为1,所以会出现两次,原本a的字符个数为8/(3-1)=4。 其他类推

(7).如何判断答案不存在?

将计算出的原来的字符串ss,按照给定的方式再拼接一次,自己拼结构的串为sss,如果sss=s,自己拼接后的串等于题目给定的拼接后的串,说明答案有效,否则输出-1

代码:

#include<iostream>
#include<map>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=5*1e5+10;

int main()
{
    int t;
    cin>>t;
    while (t--)
    {
        string s;
        cin>>s;
        vector<char>p;
        map<char,int>mp;
        for(int i=s.size()-1;~i;i--)
        {
            if(!mp[s[i]]) p.push_back(s[i]);
            mp[s[i]]++;
        }

        int len=0;
        for(int i=0;i<p.size();i++)
        {
            len+=mp[p[i]]/(p.size()-i);
        }
        string ss=s.substr(0,len);//复原而成的原字符串
        
       // cout<<"原来的"<<ss<<endl;
        

        string sss=ss;//sss是拼结后的字符串
        string t=ss;
        for(int i=p.size()-1;~i;i--)
        {
            string add;
            for(int j=0;j<t.size();j++)
            {
                if(p[i]!=t[j]) add+=t[j];
            }
            sss+=add;
            t=add; 
        }
        //cout<<"拼接后的"<<sss<<endl;

        if(sss!=s) puts("-1");
        else {
            cout<<ss<<" ";
            for(int i=p.size()-1;~i;i--)
                cout<<p[i];
                puts("");
        }
    }
    
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值