今天是泉五之旅的最后一天,明天再上机一天就要开始上学了,可怕的英语又来了,唉。
本来今天想好好考一下,没想到基本爆零,还被帅康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;
}
总得说这一次泉五之旅还是不错的,让我知道了许多自己的不足,并且认识到了许多解题的思路,因为时间原因前几天总结没写,会在之后的几天陆续补上希望对大家帮助,可以和大家一起进步。