2018 08 23 总结

今天是泉五之旅的最后一天,明天再上机一天就要开始上学了,可怕的英语又来了,唉。
本来今天想好好考一下,没想到基本爆零,还被帅康dis了。
话不多今天考的还好,主要是思维的转变,知识点分别是数论(其实就是自己找规律推)、广搜、DP
下面上题目:

第1题 点
【问题描述】
有N个二维坐标上的整数点(Xi,Yi)。现在请你选择一个整数点(X,Y)。最小化距离

【输入文件】newbarn.in
第一行一个整数N。
接下来N行,每行两个整数Xi,Yi。
【输出文件】newbarn.out
两个整数。最小距离和可行位置个数。
【输入样例】
4
1 -3
0 1
-2 1
1 -1
【输出样例】
10 6
【数据规模】
2<=N<=10000;其他数字绝对值小于等于10000。

第2题 计蒜姬
【问题描述】
兔纸们有一个计蒜姬,奇怪的是,这个计蒜姬只有一个寄存器X。兔纸们每次可以把寄存器中的数字取出,进行如下四种运算的一种后,将结果放回寄存器中。
1.
2.
3.
4.
已知初始时寄存器里的值为A,兔纸们想要知道,是否能通过若干次操作,使得最终寄存器里的值是B。如果可能,它们还想知道最少的操作次数。
【输入文件】
输入文件great.in包含两个正整数A,B。
【输出文件】
输出文件great.out一个整数,即最少操作次数,如果不存在方案,则输出-1。
【输入样例】
3 4
【输出样例】
3
【样例解释】
第一次:3 / 3 = 1
第二次:1 + 1 = 2
第三次:2 * 2 = 4
【数据规模和约定】
对于40%的数据, A,B ≤ 1000;
对于100%的数据,1 ≤ A,B ≤ 1000000000。

第3题 序列和

【问题描述】
N个数排成一个环,请选出不超过K段的连续的数,段与段间不能重叠,且
使得选出的数和最大。
【输入文件】
输入文件sum.in第一行包含两个正整数N和k。
接下来1行描述这N个数。
【输出文件】
输出文件sum.out包含一个数,即要去的最大的和。
【输入样例1】
9 2
2 -1 2 -1 2 -4 1 -1 2
【输出样例1】
7
【数据范围】
20%:K=1,N<=1000
另外20%:K=1
另外20%:N<=1000
100%:K<=10,N<=100000

第一题:
这题主要是自己推一开始发现如果只是算公式的话真的好难(不排除一些数学大佬一眼看出),我是一开始先爆搜一遍,然后把爆搜出来的点的坐标输出来,发现这些点是很有规律的,这个规律和输入点的个数有关,然后我们分析规律,因为横纵坐标之间互不影响(或者说影响不大),所以我们把它们拆开来算,首先是横坐标,我们发现当输入的点数为奇数时,答案的点数只有一个,而答案的横坐标刚好是这些点的横坐标排序后中间的那个,而纵坐标也一样,然后再看偶数始,我们发现这时个数就不一了,我们把图画出来,惊奇地发现这些点刚好组成一个矩阵,然后看矩阵的边界,又发现边界刚好对应横纵坐标分别排序后最中间的两个,这时规律就出来了。还有一个点,如果发现这个规律奇数直接输出就可以了,可偶数呢,那是一个矩阵,我一开始是直接搜索,可有一个点会TLE因为这样是O(n^2),如果卡你会爆,所以换个思路,既然这个矩阵都是答案,那么我们先随便拿一个去计算最小值,然后再计算一下矩阵中有多少个点就可以了
下面放代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int minx,miny,maxx,maxy,zx[20000],zy[20000],ans=200000001,much=0,n;
struct edge
{
    int x,y;
}d[20000];//结构体存点好像没什么用 
int js(int i,int j)//根据给的公式计算 
{
    int anser=0,x,y;
    for(int k=1;k<=n;k++)
    {
        x=d[k].x-i;
        if(x<0)
            x=0-x;
        y=d[k].y-j;
        if(y<0)
            y=0-y;
        anser+=x;
        anser+=y;
    }
    return anser;
}
int main()
{
    int i,j,x,y;
//  freopen("newbarn.in","r",stdin);
//  freopen("newbarn.out","w",stdout);
    scanf("%d",&n);
    for(i=1;i<=n;i++)//读入 
    {
        scanf("%d %d",&x,&y);
        d[i].x=x;
        d[i].y=y;
        zx[i]=x;
        zy[i]=y;
    }
    sort(zx+1,zx+1+n);//sort排一下序 
    sort(zy+1,zy+1+n);
    if(n%2==0)//判断情况 
    {//偶数时 
        int mid=n/2;
        minx=zx[mid];
        miny=zy[mid];
        mid++;
        maxx=zx[mid];
        maxy=zy[mid];
        ans=js(minx,miny);
        much=(maxx-minx+1)*(maxy-miny+1);
    }
    else//奇数时 
    {
        int mid=(n+1)/2;
        minx=zx[mid];
        miny=zy[mid];
        maxx=zx[mid];
        maxy=zy[mid];
        ans=js(minx,miny);
        much=1;
    }
    printf("%d %d",ans,much);
    return 0;
}

第二题:
这一题是经典的广搜题(难度都在思路的转换)
这一题最好是一开始自己动手算算,你会发现第二个条件是假的,根本都不会用,然后第四个也只会用一次,这样原本要四个向的广搜转化成了三个,这样还不可以,不过也有50分,其他是爆空间(反正我是),再转换一下谁说一定要从a广搜到b了,如果我们从b广搜到a的话,第一个条件就可以看成除2,第三个就是开方,当然有条件,条件是要求它们全可以整的计算才可以,而第四个我们可以看成如果b可以广搜到1,那么再加一次就可以到a(因为a可以经过一次计算到1),这样这一题就AC了
代码:

#include<iostream>
#include<cmath>
using namespace std;
int tou=0,wei=0;
struct edge
{
    int x,y;
}a[50000000];//用结构体,x表示要计算的值,y表示要经过几次计算 
int main()
{
    long long c,b;
    cin >>c>>b;
    a[wei].x=b;
    a[wei].y=0;
    wei++;
    while(tou<wei)//广搜 
    {
        int y=a[tou].x,z;
        z=(int)(sqrt(y));//开下方并且向下取整 
        if(z*z==y)//如果可以整开方 
        {
            a[wei].x=z;
            a[wei].y=a[tou].y+1;
            wei++;
        }
        if(y%2==0)//如果可以整除2 
        {
            a[wei].x=y>>1;
            a[wei].y=a[tou].y+1;
            wei++;
        }
        if(y==c)//搜到了直接输出 
        {
            cout <<a[tou].y;
            return 0;
        }
        if(y==1)//搜到1的话要再加一 
        {
            cout <<a[tou].y+1;
            return 0;
        }
        tou++;
    }
    cout <<"-1";//没搜到的话直接输出-1 
    return 0;
}

第三题:
这一题有点味道,为了简化问题我们先不看做环状,我们当成线性来做(这一点很重要),线性的话就很好DP了,我们定义f[i][j][0]表示取前i个用j段最后一个不取,f[i][j][1]表示取前i个用j段最后一个取,这样就可以写出状态转移方程f[i][j][0]=max(f[i][j-1][0],max(f[i-1][j][0],f[i-1][j][1[)),f[i][j][1]=max(f[i][j-1][1],max(f[i-1][j][1],f[i-1][j-1][0]))这样就ojbk了,但是这一题没这么简单,我们还要转换成环状,我们定义f1和f0两个数组进行DP,f1表示第一个数强行选,f0表示最后一个强制选,其他的就和线性差不多,细节在代码中注释。
上代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long f1[200001][12][2],f0[200001][12][2],a[200001];
int main()
{
    int n,k;
    memset(f1,-127,sizeof(f1));//初始化 
    memset(f0,-127,sizeof(f0));
    cin >>n>>k;
    for(int i=1;i<=n;i++)
        cin >>a[i];
    f1[1][1][1]=a[1];//边界表示第一个强制选,没别的选着 
    for(int i=2;i<=n;i++)//其他一样 
    {
        for(int j=1;j<=k+1;j++)
        {
            f1[i][j][0]=max( f1[i][j-1][0] , max( f1[i-1][j][0] , f1[i-1][j][1] ) );
            f1[i][j][1]=max( f1[i][j-1][1] , ( max( f1[i-1][j-1][0] , f1[i-1][j][1] ) + a[i] ) );
        }
    }
    f0[1][0][0]=0;//f0边界,第一个就不选了 
    for(int i=2;i<=n;i++)//其他一样 
    {
        f0[i][0][0]=f0[i-1][0][0];//注意这边因为初始化为一个很小的数,所以边界要传一下 
        for(int j=1;j<=k;j++)
        {
            f0[i][j][0]=max(f0[i][j-1][0],max(f0[i-1][j][0],f0[i-1][j][1]));
            f0[i][j][1]=max(f0[i][j-1][1],(max(f0[i-1][j-1][0],f0[i-1][j][1])+a[i]));
        }
    }
    cout <<max(max(f1[n][k+1][1],f1[n][k][0]),max(f0[n][k][0],f0[n][k][1]));//找答案,环状可以看成比线型多一个选择,就是最后一个连到第一个,
    //可以看成第一个强制选然后多一个段,段在最后一个数那里就搞定了 
    return 0;
}

总得说这一次泉五之旅还是不错的,让我知道了许多自己的不足,并且认识到了许多解题的思路,因为时间原因前几天总结没写,会在之后的几天陆续补上希望对大家帮助,可以和大家一起进步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值