2018 8 18 学习总结

版权声明:八月炊火的博客如需转载请注明出处 https://blog.csdn.net/qq_34990731/article/details/81913327

今天去了泉州五中,感受了一下计算机的高难度题目,好难啊,太变态了,第一题是数论只能背代码,没办法,暴力也只有20分左右,第二题是DP,这一题就很有难度,一般情况下能拿到50分,可想要更高就难了,第三题是广搜,这一题一开始想了一个DP,只有80分,原来是有bug,正解是广搜,下面上一下题目,大家感受一下。
测试链接:第一题
1.方程的解(equation.pas/c/cpp)

【问题描述】
佳佳碰到了一个难题,请你来帮忙解决。
对于不定方程a1+a2+……+ak=g(x),其中k>=2且k∈N*,x是正整数,g(x)=x^x mod 1000(即x^x除以1000的余数),x,k是给定的数。我们要求的是这个不定方程的正整数解组数。
举例来说,当k=3,x=2时,分别为(a1,a2,a3)=(2,1,1),(1,2,1),(1,1,2).
【文件输入】
  输入文件equation.in有且只有一行,为用空格隔开的两个正整数,依次为k,x。
【文件输出】
  输出文件equation.out有且只有一行,为方程的正整数解组数。
【样例输入】
  3 2
【样例输出】
  3
【数据范围】
对于40%的数据,ans<=10^16;
对于100%的数据,k<=100,x<=2^31-1,k<=g(x)。

2.打砖块(game.pas/c/cpp)

【题目描述】
小红很喜欢玩一个叫打砖块的游戏,这个游戏的规则如下:
在刚开始的时候,有n行*m列的砖块,小红有k发子弹。小红每次可以用一发子弹,打碎某一列当前处于这一列最下面的那块砖,并且得到相应的得分。
如图所示:
这里写图片描述
某些砖块在打碎以后,还可能将得到一发子弹的奖励。最后当所有的砖块都打碎了,或者小红没有子弹了,游戏结束。
小红在游戏开始之前,就已经知道每一块砖在打碎以后的得分,并且知道能不能得到一发奖励的子弹。小红想知道在这次游戏中她可能的最大得分,可是这个问题对于她来说太难了,你能帮帮她吗?
【输入格式】
第一行有3个正整数,n,m,k。表示开始的时候,有n行*m列的砖块,小红有k发子弹。
接下来有n行,每行的格式如下:f1 c1 f2 c2 f3 c3 …… fm cm
其中fi为正整数,表示这一行的第i列的砖,在打碎以后的得分。ci为一个字符,只有两种可能,Y或者N。Y表示有一发奖励的子弹,N表示没有。
所有的数与字符之间用一个空格隔开,行末没有多余的空格。
【输出格式】
仅一个正整数,表示最大的得分。
【输入样例】
3 4 2
9 N 5 N 1 N 8 N
5 N 5 Y 5 N 5 N
6 N 2 N 4 N 3 N
【输出样例】
13
【数据规模】
对于20%的数据,满足1<=n,m<=5,1<=k<=10,所有的字符c都为N
对于50%的数据,满足1<=n,m<=200,1<=k<=200,所有的字符c都为N
对于100%的数据,满足1<=n,m<=200,1<=k<=200,字符c可能为Y
对于100%的数据,所有的f值满足1<=f<=10000

3.渡河(river.pas/c/cpp)
【题目描述】
传说中教主乃世外高人,不屑于参加OI竞赛,于是云游四方,威风八面。只不过教主行踪不定,就像传说中的神兽一样可遇而不可求。小L和小H为了求得教主签名的Orz教主T-Shirt,打算碰碰运气展开了冒险。在冒险中,他们不幸被突来的洪水冲到了一个神秘丛林中,他们想尽快逃出这个地方。小L找到了一张看似为曾经的冒险者遗弃的地图,但经过探查,地图所示的确实是这片丛林。小L从地图上看到,有众多河流穿过这片丛林,等到他接近一条最近的河流时,发现水流较急,且河水很深,小H不擅长游泳,所以他们决定利用丛林中的树木做一只竹筏渡河。
虽然竹筏做好后可以在这一条河所连通的水域任意行进,但是竹筏在上岸后必须抛弃,若想再次渡河必须再做一次竹筏,但这毕竟是十分辛苦的,他们希望做竹筏也就是渡河的次数尽量少,就求助于你。
地图上的陆地和河流可以抽象冲一个n*n由数字0和1组成的矩阵,其中0代表陆地,1代表河流。无论在陆地上还还是河流上,他们都可以向相邻8格(边相邻或角相邻)移动,但是若要从陆地进入河流(也就是从0到1),则必须制作竹筏。若到达地图边界则顺利逃脱。但是小T和小K有可能迷路,所以会多次询问你,对于每次询问,只要输出到达地图边界需要的最少渡河次数即可,保证每次询问都是指向陆地。
小L和小H翻到地图的反面,赫然发现六个大字:“教主到此一游”!两人无法抑制自己激动的心情,将这张地图珍藏起来。据说后来这张图成为无价之宝。
【输入格式】
第1行包括2个正整数N,K,分别描述了地图的长宽以及询问的次数。
下面N行,每行N个数字0或者1,数字之间没有空格,描述了这张地图。
接下来K行,每行2个正整数xi,yi,询问在第xi行第yi列最少需要渡河几次。
【输出格式】
  输出仅包括1行,按输入顺序每行对于一个询问输出最少需要渡河的次数,数字间用空格隔开,行末换行并没有空格。
【样例输入】
  9 3
000000000
011111110
010101010
011000110
010000010
010111010
010101010
011111110
000000000
1 3
3 3
4 6
【样例输出】
0 1 1
【样例说明】
第1次询问由于已经处于边界所以答案为0。
第2次询问不断向左或向上走都只要渡河1次。
第3次询问不断向四个方向中的一个方向走同样只需要1次渡河。
【数据规模】
对于20%的数据,有n≤10;
对于40%的数据,有n≤100,k≤10;
对于60%的数据,有n≤1000,k≤100;
对于100%的数据,有n≤1000,k≤40000。

这三题我都认为不错,总共涉及的知识点是组合数,快速幂,DP,广搜
第一题就比较简单,只要会模板就行了,首先用快速幂处理一下x^x mod1000,再用组合数,至于组合数是什么在此不详谈,下面上代码(有注释)

#include<iostream>
using namespace std;
int a[10001];
void zhs(int n,int m) //组合数求Cn,m  不会的去背一下吧   a数组是答案,在此因为答案太大,所以用高精 
{
    a[1]=1;
    a[0]=1;
    for(int i=m+1;i<=n;i++)
    {
        for(int j=1;j<=a[0];j++)
        {
            a[j]*=i;
        }
        int tmp=0;
        for(int j=1;j<=a[0];j++)
        {
            tmp+=a[j];
            a[j]=tmp%10;
            tmp/=10;
        }
        while(tmp)
        {
            a[++a[0]]=tmp%10;
            tmp/=10;
        }
    }
    for(int i=2;i<=n-m;i++)
    {
        int tmp=0;
        for(int j=a[0];j>=1;j--)
        {
            tmp*=10;
            tmp+=a[j];
            a[j]=0;
            if(tmp>i)
            {
                a[j]=tmp/i;
                tmp%=i;
            }
        }
        while(a[a[0]]==0) a[0]--;
    }
}
long long ksm(int a,int b,int mod)//快速幂模板,没难度 
{
    int rec=1;
    while(b)
    {
        if(b&1)
        {
            rec=rec*a%mod;
        }
        a=a*a%mod;
        b>>=1;
    }
    return rec;
}
int main()
{
    int x,k,i,j,n;
    long long ans=1;
    freopen("equation.in","r",stdin);
    freopen("equation.out","w",stdout);
    cin >>k>>x;
    x=x%1000;//先mod一下,免得直接炸了 
    n=ksm(x,x,1000);
    zhs(n-1,k-1);
    for(i=a[0];i>=1;i--)//然后就可以开心地输出答案了 
        cout <<a[i];
    return 0;
}

第二题就有些难度,前50分直接DP就可以了,因为没有奖励,所以比较简单,下面说一下满分做法,50分是开两个数组DP,100分的区别就是有没有奖励所以我们多开两个来区别
定义状态:
sn[j][i]:第j列打i发子弹,最后一个子弹打第j列的砖块。
sy[j][i]:第j列打i发子弹,最后一个子弹不打第j列的砖块。
bn[j][i]:前j列打i发子弹,最后一个子弹打前j列的砖块。
by[j][i]:前j列打i发子弹,最后一个子弹不打第j列的砖块。
下面上代码,其他的请看注释

#include<iostream>
#include<cstdio>
using namespace std;
int n,m,k;
int  a[201][201],sy[201][201],sn[201][201],by[201][201],bn[201][201];
char c[201][201];
int main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin>>a[i][j]>>c[i][j];
        }
    }
    int t;
    for(int i=1;i<=m;i++)//先处理一下sn,sy 
    {
        t=n; 
        while(t>0&&c[t][i]=='Y')//至于奖励因为要打完才可以得到,这是重点所以我们才要多开两个 
        {                       //如果我们还有一个或跟多子弹那么直接加就可以了(相当于这个不用子弹) 
            sy[i][0]+=a[t][i];  //而如果没了的话我们就要考虑一下最后一个到底要不要打这里 
            t--;
        }
        for(int j=1;j<=n&&t>0;j++)
        {
            sn[i][j]=sy[i][j-1]+a[t][i];
            sy[i][j]=sn[i][j];
            t--;
            while(t>0 && c[t][i]=='Y')
            {
                sy[i][j]+=a[t][i];
                t--;
            }
        }
    }
    int tmp;
    for(int j=1;j<=m;j++)//处理bu,bn和上面sn,sy一样 
    {
        for(int i=0;i<=k;i++)
        {
            for(int l=0;l<=n;l++)
            {
                if(l<=i)
                {
                    tmp=by[j-1][i-l]+sy[j][l];
                    if(tmp>by[j][i])
                    {
                        by[j][i]=tmp;
                    }
                    if(l>0)
                    {
                        tmp=by[j-1][i-l]+sn[j][l];
                        if(tmp>bn[j][i]) bn[j][i]=tmp;
                    }
                    if(i-l>0)
                    {
                        tmp=bn[j-1][i-l]+sy[j][l];
                        if(tmp>bn[j][i]) bn[j][i]=tmp;
                    }
                }
            }
        }
    }
    cout<<bn[m][k];//开开心心地输出 
    return 0;
}

第三题是一道不错的广搜题,我们开两个队列来存,一个全部存1,一个全部存0(都是存坐标,建议用结构体),然后我们去广搜,把所有联通的1,0按之前说的存(边界要为0),然后从0的联通块处理,然后再处理0,每次处理时找到的1,0分开存,而处理到0时所有都要加上1,意味着从陆地到水里,至于为什么,等你手动算一遍就知道了,然后我们把整张图都算出来了,题目问什么直接输出就可以了

#include<iostream>
#include<cstdio>
using namespace std;
struct yuji
{
    int x,y;
};
int dx[9]={0,0,1,0,-1,1,1,-1,-1};//方向 
int dy[9]={0,1,0,-1,0,-1,1,-1,1};//方向 
int a[1001][1001],b[1001][1001],f[1001][1001];
yuji que1[1000401];
yuji que2[1000401];
int n,k;
int main()
{
    freopen("river.in","r",stdin);
    freopen("river.out","w",stdout);
    cin>>n>>k;
    char ch[10000];
    for(int i=1;i<=n;++i)//快读算法,使读入优化,当读入数据很多时就要这样,否则TLE 
    {
        scanf("%*");
        scanf("%s",ch);
        for(int j=0;j<n;++j)
        a[i][j+1]=ch[j]-'0';
    }
    int l1=0,r1=1,r2=0;
    que1[1].x=0;
    que1[1].y=0;
    int level=1,last=0;
    b[0][0]=1;
    while(l1<=r1)
    {
        ++l1;
        int x1=que1[l1].x;
        int y1=que1[l1].y;
        f[x1][y1]=level; 
        for(int i=1;i<=8;i++)
        {
            int xx=x1+dx[i];
            int yy=y1+dy[i];
            if(xx>=0&&xx<=n+1&&yy>=0&&yy<=n+1&&b[xx][yy]==0)
            {
                if(a[x1][y1]==a[xx][yy])//如果是同类,例如1和1放一起,0和0放一起 
                {
                    b[xx][yy]=1;
                    f[xx][yy]=level;
                    ++r1;
                    que1[r1].x=xx;
                    que1[r1].y=yy;
                }
                else
                {
                    ++r2;
                    que2[r2].x=xx;
                    que2[r2].y=yy;
                    b[xx][yy]=1;
                }
            }
        }
        if(l1==r1)//处理完第一个队列,我们把第二个复制上去,这样形成滚动 
        {
            last=1-last;
            if(last==0) level++;
            for(int i=1;i<=r2;i++)
            {
                que1[i]=que2[i];
            }
            l1=0;
            r1=r2;
            r2=0;
        }
    }
    for(int i=1;i<=k;i++)//输出答案 
    {
        int x,y;
        cin>>x>>y;
        cout<<f[x][y]-1<<" ";
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

希望对大家有帮助,如果还是不懂欢迎留言询问

猜你喜欢

转载自blog.csdn.net/qq_34990731/article/details/81913327