伊苏比的梦幻之旅(二)比赛题解

比赛地址:http://qscoj.cn/contest/22/

第5-1关 吃饼(小数据)

分析:数据的范围只有1-3,所以可以将每种情况手工计算出来。1刀最多可切2块,2刀可切4块,3刀可切7块,4刀可切11块。注意,本题的坑点在于在场的女性除了小卿卿的F位朋友,还包括小卿卿自己,所以吉祥饼上得切F+1刀。

标程:

#include<bits/stdc++.h>
using namespace std;
int piece(int k)
{
    if (k==1) return 2;
    if (k==2) return 4;
    if (k==3) return 7;
    if (k==4) return 11;
}
int main()
{
    int m,f;
    cin>>m>>f;
    cout<<piece(m)+piece(f+1)<<endl;
    return 0;
}

第5-2关 吃饼(中数据)

分析:设切N刀的最多饼块数为S(N),由第5-1关得S(1)=2;S(2)=4;S(3)=7;S(4)=11可以推出S(N)-S(N-1)=N,由此可以计算出S(N)的通项公式为S(N)=(N^2+N+2)/2,N虽然为10^9大小,但设变量时还是建议设成long long,不然相乘时会爆int.

标程:

#include<bits/stdc++.h>
using namespace std;
long long S(long long k)
{
    return (k*k+k+2)/2;
}
int main()
{
    long long m,f;
    cin>>m>>f;
    cout<<S(m)+S(f+1)<<endl;
    return 0;
}

第5-3关 吃饼(大数据)

分析:由第5-2关可知,S(N)=(N^2+N+2)/2,这题N的大小为10^2333,故使用高精度的加法、乘法、除法便可解决此题。此题运算操作较多,需注意下进位的问题。或用JAVA中的大整数也可以解决这个问题。

C++标程:

#include<bits/stdc++.h>
using namespace std;
int m[2500],f[2500];
int m1[2500],f1[2500];
int c1[5000],c2[5000],c3[5000];
int main()
{
    int i,j,lena,lenb;
    string a,b;
    bool flag;
    cin>>a>>b;
    lena=a.length();lenb=b.length();
    for(i=0;i<lena;i++)
        m[lena-1-i]=a[i]-48;
    for(i=0;i<lenb;i++)
        f[lenb-1-i]=b[i]-48;
    f[0]++;j=0;
    while (f[j]==10) {f[j]=0;j++;f[j]++;}
    for(i=0;i<2400;i++)
    {
        m1[i]=m[i];f1[i]=f[i];
    }
    m1[0]++;j=0;
    while (m1[j]==10) {m1[j]=0;j++;m1[j]++;}
    f1[0]++;j=0;
    while (f1[j]==10) {f1[j]=0;j++;f1[j]++;}
    for(i=0;i<2400;i++)
     for(j=0;j<2400;j++)
     {
        c1[i+j]+=m[i]*m1[j];
        c2[i+j]+=f[i]*f1[j];
     }
    for(i=0;i<4800;i++)
        c3[i]=c1[i]+c2[i];
    for(i=0;i<4800;i++)
    {
        j=c3[i]/10;c3[i]%=10;c3[i+1]+=j;
    }
    for(i=0;i<4800;i++)
    {
        if (c3[i]%2==0) c3[i]/=2;
        else {c3[i-1]+=5;c3[i]/=2;}
    }
    c3[0]+=2;j=0;flag=false;
    while(c3[j]>=10) {c3[j]-=10;j++;c3[j]++;}
    for(i=4800;i>=0;i--)
    {
        if (i==0 || c3[i]) flag=true;
        if (flag) printf("%d",c3[i]);
    }
    cout<<endl;
    return 0;
}

JAVA标程:

import java.util.*;
import java.math.*;
public class Main{
     public static void main(String args[]){
        Scanner cin=new Scanner(System.in);
        BigInteger m,f,m1,f1,ans1,ans2,ans;
        m=cin.nextBigInteger();f=cin.nextBigInteger();
        f=f.add(BigInteger.valueOf(1));
        m1=m.add(BigInteger.valueOf(1));
        f1=f.add(BigInteger.valueOf(1));
        ans1=m.multiply(m1);
        ans1=ans1.add(BigInteger.valueOf(2));
        ans1=ans1.divide(BigInteger.valueOf(2));
        ans2=f.multiply(f1);
        ans2=ans2.add(BigInteger.valueOf(2));
        ans2=ans2.divide(BigInteger.valueOf(2));
        ans=ans1.add(ans2);
        System.out.println(ans);
    }
}

第6-1关 末位数(小数据)

分析:求M^N的个位数,M和N只有10^4大小,故直接for循环一遍,每次相乘后%10就是最终的答案。

标程:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int m,n,k,i;
    cin>>m>>n>>k;
    for(i=1;i<=n;i++)
    {
        k*=m;k%=10;
    }
    cout<<k<<endl;
}

第6-2关 末位数(中数据)

分析:求M^N的个位数,M和N有10^13大小,直接for肯定超时,所以我们可以通过分析下M的个位数,对于每一个M它的前几项的个位数找出他的规律。不难发现,个数为0、1、5、6的数,无论多少次方,其个位数还是其本身;个位数为2、3、7、8的数的乘方的个位数以4为周期在变化;个位数为4、9的数的乘方的个位数以2为周期在变化。分析出每一种情况,再特判一下任何一个数的0次方均为1,就可以AC了。

标程:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    long long m,n,k;
    cin>>m>>n>>k;
    if (n==0) {cout<<1<<endl;return 0;}
    m%=10;n%=4;
    if (m==0) k=0;
    if (m==1) k=1;
    if (m==2)
    {
        if (n==0) k=6;
        if (n==1) k=2;
        if (n==2) k=4;
        if (n==3) k=8;
    }
    if (m==3)
    {
        if (n==0) k=1;
        if (n==1) k=3;
        if (n==2) k=9;
        if (n==3) k=7;
    }
    if (m==4)
    {
        if (n==0) k=6;
        if (n==1) k=4;
        if (n==2) k=6;
        if (n==3) k=4;
    }
    if (m==5) k=5;
    if (m==6) k=6;
    if (m==7)
    {
        if (n==0) k=1;
        if (n==1) k=7;
        if (n==2) k=9;
        if (n==3) k=3;
    }
    if (m==8)
    {
        if (n==0) k=6;
        if (n==1) k=8;
        if (n==2) k=4;
        if (n==3) k=2;
    }
    if (m==9)
    {
        if (n==0) k=1;
        if (n==1) k=9;
        if (n==2) k=1;
        if (n==3) k=9;
    }
    cout<<k<<endl;
    return 0;
}

第6-3关 末位数(大数据)

分析:这个题要求M^N的后k位数,其实就是M^N%P的问题,用到的知识点是快速幂。其中,P=10^k,注意输出时通过一些判断来保证有k位。

#include<bits/stdc++.h>
using namespace std;
long long modexp(long long a,long long b,int mod)
{
    long long res=1;
    while (b)
    {
        a=a%mod;
        if (b & 1) res=res*a%mod;
        b=b>>1;
        a=a*a%mod;
    }
    return res;
}
int main()
{
    long long m,n,k,ans;
    cin>>m>>n>>k;
    if (k==1)
    {
        ans=modexp(m,n,10);
        printf("%lld",ans);
    }
    if (k==2)
    {
        ans=modexp(m,n,100);
        if (ans<10) printf("0");
        printf("%lld
",ans);
    }
    if (k==3)
    {
        ans=modexp(m,n,1000);
        if (ans<100) printf("0");
        if (ans<10) printf("0");
        printf("%lld
",ans);
    }
    if (k==4)
    {
        ans=modexp(m,n,10000);
        if (ans<1000) printf("0");
        if (ans<100) printf("0");
        if (ans<10) printf("0");
        printf("%lld
",ans);
    }
    if (k==5)
    {
        ans=modexp(m,n,100000);
        if (ans<10000) printf("0");
        if (ans<1000) printf("0");
        if (ans<100) printf("0");
        if (ans<10) printf("0");
        printf("%lld
",ans);
    }
    return 0;
}

第7-1关 排队(小数据)

分析:数据范围只有3,手工计算所有情况,发现只有“3 1 2”这种情况不行,便特判下这种情况输出“Fail”,其他均输出“Success”.

标程:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int a[4],i,n;
    cin>>n;
    for(i=1;i<=n;i++)
        cin>>a[i];
    if (n==3 && a[1]==3 && a[2]==1 && a[3]==2) cout<<"Fail"<<endl;
    else cout<<"Success"<<endl;
    return 0;
}

第7-2关 排队(中数据)

分析:数据范围有10,可以通过DFS方式把所有的入栈出栈的可能计算出来,再判断是否与汪老师要求的序列进行匹配。

标程:

#include<bits/stdc++.h>
using namespace std;
int s[25],a[13],b[13],n;
int t[2];
bool flag;
stack<int> st;
void dfs(int k)
{
    int i,j,cnt;
    if (k==2*n+1)
    {
        if (t[0]!=t[1]) return ;
        cnt=0;j=1;while(!st.empty()) st.pop();
        for(i=1;i<=2*n;i++)
        {
            if (s[i]==0) st.push(j++);
            else {cnt++;b[cnt]=st.top();st.pop();}
        }
        bool g;
        g=true;
        for(i=1;i<=n;i++)
            if (a[i]!=b[i]) g=false;
        if (g) flag=true;
        return ;
    }
    for(i=0;i<2;i++)
    {
        s[k]=i;t[i]++;
        if (t[1]<=t[0]) dfs(k+1);
        t[i]--;
    }
}
int main()
{
    int i;
    cin>>n;
    for(i=1;i<=n;i++)
        cin>>a[i];
    flag=false;dfs(1);
    if (flag) cout<<"Success"<<endl;
    else cout<<"Fail"<<endl;
    return 0;
}

第7-3关 排队(大数据)

分析:其实这道题就是一道模拟题。用一个光标记录新队列当前元素的位置,一开始这个位置为1,先将原队列的头元素压入栈中,再不断判断栈顶元素是否为新队列指向的元素,如果是的话就弹出栈顶元素,并且将新队列位置右移;不是的话就再从原队列中压入元素。最后检查栈是否为空或者光标是否移到最右边的位置,来判断是否排队成功。

标程:

#include<bits/stdc++.h>
using namespace std;
stack<int> q;
int main()
{
    int n,i,j;
    int a[60010];
    cin>>n;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    j=1;
    for(i=1;i<=n;i++)
    {
        q.push(i);
        while(!q.empty() && q.top()==a[j]) {q.pop();j++;}
    }
    if (q.empty()) cout<<"Success"<<endl;
    else cout<<"Fail"<<endl;
    return 0;
}

第8-1关 默契值(小数据)

分析:如果只有2名队员,答案就是他们的默契值;如果有4名队员,刘老师必须得跟其中与自己默契值最大的队员组队,在合理的可能中,再判断剩下两名队员哪组默契值最小,两种默契值相加就是答案。

标程:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int a,b,c,d,e,f,n,maxx,minx;
    cin>>n;
    if (n==2)
    {
        cin>>a;
        cout<<a<<endl;
        return 0;
    }
    cin>>a>>b>>c>>d>>e>>f;
    maxx=max(a,max(b,c));minx=0x3f3f3f3f;
    if (a==maxx) minx=min(minx,f);
    if (b==maxx) minx=min(minx,e);
    if (c==maxx) minx=min(minx,d);
    cout<<maxx+minx<<endl;
    return 0;
}

第8-2关 默契值(中数据)

分析:队员最大有10名,所以可以用全排列的方法做这道题。奇偶相邻的两名队员便为配对的队员,得保证刘老师得跟与自己默契值最大的队员配对,再从剩下的队员找配对的最小默契值和。还是注意特判下N=2的情况。

标程:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,i,j,k,maxx,minx,res;
    int a[25][25],b[25];
    cin>>n;
    for(i=1;i<=n-1;i++)
        for(j=1;j<=n-i;j++)
            cin>>a[i][i+j];
    if (n==2)
    {
        cout<<a[1][2]<<endl;
        return 0;
    }
    maxx=-1;minx=0x3f3f3f3f;
    for(i=2;i<=n;i++)
        maxx=max(a[1][i],maxx);
    for(i=1;i<=n;i++)
        b[i]=i;
    do
    {
        if (a[1][b[2]]<maxx) continue;res=0;
        for(i=2;i<=n/2;i++)
        {
            j=b[2*i-1];k=b[2*i];
            res+=a[min(j,k)][max(j,k)];
        }
        minx=min(res,minx);
    }while(next_permutation(b+2,b+1+n));
    cout<<maxx+minx<<endl;
    return 0;
}

第8-3关 默契值(大数据)

分析:N的大小为20,用全排列或者DFS的方式肯定会超时。所以可以采用DP+记忆化搜索的方式来完成此题。需要开一个1<<20的数组,将每个数转化为二进制,如果某一位的值为0,则表明该种情况不包括其位置所对应的队员;如果某一位的值为1,则表明该种情况包括其位置所对应的队员。初始化时将d[x]所有值赋为1个非常大的数,将d[0]赋为0.在dp函数中每次先找出当前没配对的第1名队员,再从剩下没配对的队员中依次寻找与它配对,将s一开始赋值为(1<<n)-1;s=s^(1<<i)^(1<<j)就表示去掉第i名队员和第j名队员剩下的情况。转移方程便为d[s]=min(d[s],dp(s^(1<<i)^(1<<j))+a[i][j]);总之,只要将这道题的方法搞清楚了,解决它也不是什么难事。

标程:

#include<bits/stdc++.h>
using namespace std;
int a[20][20],d[1<<20];
int n;
int inf=0x3f3f3f3f;
int dp(int s)
{
    int i,j;
    if (d[s]!=inf) return d[s];
    for(i=0;i<n;i++)
        if (s & (1<<i)) break;
    for(j=i+1;j<n;j++)
        if (s & (1<<j)) d[s]=min(d[s],dp(s^(1<<j)^(1<<i))+a[i][j]);
    return d[s];
}
int main()
{
    int t,s,maxx,minx,i,j;
    cin>>n;
    for(i=0;i<n-1;i++)
     for(j=1;j<n-i;j++)
      cin>>a[i][i+j];
    if (n==2)
    {
        cout<<a[0][1]<<endl;
        return 0;
    }
    maxx=-1;minx=inf;
    for(i=1;i<n;i++)
        if (a[0][i]>maxx) maxx=a[0][i];
    for(i=1;i<n;i++)
    {
        if (a[0][i]==maxx)
        {
            for(j=0;j<(1<<n);j++)
                d[j]=inf;
            s=(1<<n)-1;
            s=s^(1<<0)^(1<<i);
            d[0]=0;
            minx=min(minx,dp(s));
        }
    }
    cout<<maxx+minx<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/cs-lyj1997/p/6821655.html