第一题
题意:
求题目给出两个数字的乘积
分析:
这题明显只是考察学生的高精可我居然没做对,只要多练习几次,即可AC
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
inline LL read() {
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
int x1[101],x2[101],w[101];
int main()
{ freopen("bullmath.in","r",stdin);
freopen("bullmath.out","w",stdout);
string z,s;
cin>>z;
cin>>s;
int l1=z.size()-1,l2=s.size()-1;
int a=0,b=0;
if(z[0]=='-') a++;
if(s[0]=='-') b++;
if(a==1&&b==0||a==0&&b==1) printf("-");
for(int i=a;i<=l1;i++)
x1[l1-i+1]=z[i]-48;
for(int i=b;i<=l2;i++)
x2[l2-i+1]=s[i]-48;
int e,h;int l;
for(int i=1;i<=l1+1;i++)
{
e=0;
for(int j=1;j<=l2+1;j++)
{
h=(x1[i]*x2[j]+e+w[j+i-1]);
w[j+i-1]=h%10;
e=h/10;
}
if(e>0) w[l2+1+i]=e;
}
int i=100;
while(i>1&&w[i]==0) i--;
for(int j=i;j>0;j--) printf("%d",w[j]);
fclose(stdin);
fclose(stdout);
return 0;
}
第二题
题意:
求在n个牛棚中,如何安排c头牛使得相邻的两头最近的距离最远
分析:
这道题,明显不能用O(n^2),毕竟n最大可以在100,000以内,所以我们采用O(n*logn)的算法:二分+贪心
我的基本想法是这样的:先用二分算出最小距离,在每次算出后,使用贪心进行验证,如果可以,那么记录,最后输出
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
inline LL read() {
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
int n,m;
int x[100000];
bool tx(int w)
{
int now=1,last=1,t=1;
while(t<=m)
{
while(x[now]-x[last]<w&&now<=n) now++;
if(now>n&&t<m) return 0;
t++;last=now;
if(t==m) return 1;
}
return 1;
}
int main()
{
freopen("aggr.in","r",stdin);
freopen("aggr.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++) x[i]=read();
sort(x+1,x+1+n);
int l=1,r=1000000001;
int mid,minm;
while(l<=r)
{
mid=(l+r)>>1;
if(tx(mid)
{minm=mid;l=mid+1;
else r=mid-1;
}
printf("%d",minm);
fclose(stdin);
fclose(stdout);
return 0;
}
第三题
题意:
求t组数据中,每个数的约数的和
分析:
这一题,我们可以使用传统的求约数和的算法。当然这样肯定超时,所以我们可以在这个基础上加上两种优化:
(1):02优化:#pragma GCC optimize (2),虽然这种优化有点作弊的意思,且在考试时也被禁止,但不能否认其的作用之大,当然小编也极力反对用这种方法去AC题目
(2):第二种,则是记录我们已经暴力出来的数值,以后再遇到就可以直接输出,而小编就介绍一下这一种的方法
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
inline LL read() {
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
int f[5000001];
int main()
{
int n;
n=read();
int a,b;
long long t;
for(int i=1;i<=n;i++)
{
a=read();
if(f[a]!=0) {printf("%d\n",f[a]);continue;}
t=0;
b=(int)sqrt(a);
for(int j=1;j<=b;j++)
if(a%j==0)
{
t+=j;
if(a/j!=j) t+=a/j;
}
printf("%d\n",t);
f[a]=t;
}
return 0;
}
第四题
题意:
求在增加旅店数后,我们有几种方案可以到达目的地(7000km处)
分析:
在考试时,看到这题,小编脑子里不断的浮现出各种动态转移方程,因为又是放在第四题,所以小编心有余辜,不敢尝试。但在事后上了洛谷,发现只是一道入门难度的水题。再仔细看了下题目,MMP,暴力、模拟、递推……各种算法,应有尽有,而小编则采用了递推的方式
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
inline LL read() {
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
int x[10001],ans[10001];
int main()
{
int n,m;
n=read();m=read();
int a;
a=read();
x[1]=0;
x[2]=990;
x[3]=1010;
x[4]=1970;
x[5]=2030;
x[6]=2940;
x[7]=3060;
x[8]=3930;
x[9]=4060;
x[10]=4970;
x[11]=5030;
x[12]=5990;
x[13]=6010;
x[14]=7000;
for(int i=1;i<=a;i++) x[14+i]=read();
sort(x+1,x+15+a);
ans[1]=1;
for(int i=1;i<=14+a;i++)
for(int j=1;j<=i;j++)
{
int b=x[i]-x[j];
if(b>=n&&b<=m) ans[i]+=ans[j];
}
printf("%d",ans[14+a]);
return 0;
}
第五题
题意:
求有多少个符合题目特定要求的数列
分析:
这道题目需要使用dp求解。
构造f[i,j]表示(1…i)的全排列中,逆序对为j个的排列共有几个
我们首先手动模拟打表一下
f[1,0]=1 f[2,0]=1 f[3,0]=1 f[4,0]=1
f[2,1]=1 f[3,1]=2 f[4,1]=3
f[3,2]=2 f[4,2]=5
f[3,3]=1 f[4,3]=6
f[4,4]=5
f[4,5]=3
f[4,6]=1
表中有很多性质 比如:i的全排列中逆序对的个数是0…i*(i-1)/2:其实这是因为i的全排列中总共有i*(i-1)/2个数对,当这些数对全都是逆序对的时候,就得到了逆序对的最大值,
再比如:对于一个i,他的f[i,j]是关于f[i,i*(i-1)/4]呈现中心对称的(f[4,2]和f[4,4]、f[4,1]和f[4,5]、f[4,0]和f[4,6]关于f[4,3]对称):其实这是因为数对总数为i*(i-1)/2,而一个数对要么是逆序对要么是顺序对,那么我们求i的全排列中逆序对个数有j个,
等价于求i的全排列中顺序对有i*(i-1)/2-j个,显而易见,对于N的全排列 顺序对有K个这样的排列个数,和逆序对有K个这样的排列个数,是相等的于是我们有了f[i,j]=f[i, i*(i-1)/2-j]于是对称中心就是f[i,i*(i-1)/4];
最后一个重要的性质也就是我们的DP状态转移方程就是:
if i>j then f[i,j]:=sum{f[i-1,0],f[i-1,1],f[i-1,2]…f[i-1,j]}
else f[i,j]:=sum{f[i-1,j],f[i-1,j-1],f[i-1,j-2]…f[i-1,j-i+1]}
当i>j的时候,我们枚举i的全排列的第一位的数字,如果是1,那么就要求剩下i-1个数中有j个全排列,如果是2,要求剩下i-1个数中有i-2个全排列,依次类推,得到了第一个方程
当i<=j的时候,由于i的全排列中最大的数字是i,所以把i放到第一位上,由第一位最多能能产生i-1个逆序对,把1放到第一位上能产生0个逆序对,所以i-1-1+1=i-1,这时的f[i,j]就要由f[i-1,j]开始算上他自己,总共i-1项的和。
而此时,我们再对第二个方程进行一下优化,可得出:f[i][j]=f[i-1][j]+f[i-1][j-1]-f[i-1][j-i]
代码:
#include<cstdio>
#include<iostream>
using namespace std;
int n,k,f[110][10001];
int main()
{
scanf("%d%d",&n,&k);
f[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=k;j++)
if (j)
{
if (i>j) f[i][j]=(f[i-1][j]+f[i][j-1])%10000; else
f[i][j]=(f[i-1][j]+f[i][j-1]-f[i-1][j-i])%10000;
} else f[i][j]=f[i-1][j];
f[n][k]=(f[n][k]+10000)%10000;
printf("%d",f[n][k]);
}