目录
No.1(1T)龟兔赛跑/动态规划
No.2(2B)过山车/二分图最大分配
No.3(2G)解整数方程
No.4(1D)判定素数
No.5(1H)母牛个数/递归
No.6(1L)回文串/string类使用
No.7(1M)进制转换
No.8(1O)求差集/可用set类
No.9(1P) A^B后三位/规避MLE
No.10(1K)发工资
No.11(1V)找规律
No.12(2H)掏空三角形
No.13(2C)求夹角/math.h库
No.14(1U)电话号码/c风格字符串使用
No.15(1S)蜜蜂/规避TLE
No. 1 龟兔赛跑
Problem Description
龟兔赛跑,比赛是设在一条笔直的道路上,长度为L米,规则很简单,谁先到达终点谁就算获胜。
兔子匀速(VR m/s)一直跑,乌龟骑车(VT1 m/s),车需充电,每次充满电最多只能行驶C米的距离,没电以后就只能用脚来蹬了速度(VT2 m/s)。乌龟在跑道上修建了很多很多(N个)的供电站,供自己给电动车充电。其中,每次充电需要花费T秒钟的时间。当然,乌龟经过一个充电站的时候可以选择去或不去充电。
判断乌龟用最佳的方案进军时,能不能赢了一直以恒定速度奔跑的兔子。
Input
本题目包含多组测试第一行是一个整数L代表跑道的总长度
第二行包含三个整数N,C,T,分别表示充电站的个数,电动车冲满电以后能行驶的距离以及每次充电所需要的时间
第三行也是三个整数VR,VT1,VT2,分别表示兔子跑步的速度,乌龟开电动车的速度,乌龟脚蹬电动车的速度
第四行包含了N(N<=100)个整数p1,p2...pn,分别表示各个充电站离跑道起点的距离,其中0<p1<p2<...<pn<L
其中每个数都在32位整型范围之内。
题目数据保证不会出现乌龟和兔子同时到达的情况。
Output
当乌龟有可能赢的时候输出一行 “What a pity rabbit!"。否则输出一行"Good job,rabbit!";
Solution
由于兔子是匀速的,所以兔子到达终点的时间是确定的,唯一不确定的是乌龟的。将起点,终点,以及中间的N个充电站当作N+2个点。需要求解到达N+2个点的最优解。
起点时间是0.这是必须的,然后往后递归到第i个,就让j从0循环到i-1,依次代表从j站充满了电一直开到i站,这样得到到达i站所需要的最短时间。
最后比较到达第n+2站(终点)的时间与兔子所花的时间。
Detail
详见代码注释。
Summarize
对我来说,这题难度不小,本来想造个判断判断状态的函数结果发现太复杂了... 附上原始思路:
充了也骑不过去:(不剩电)
是否充电对下一段路无影响/直接判定,选最优解else(会剩下电)
充电的好处:自己段的时间节约A(不一定)和给下一次剩余的电量的增加B
如果B无意义(满电也骑不过去),则只考虑A
如果B有意义:既省时间又剩电:充 else存档:
假设充电/假设不充,回到上述过程,直到出现明确结果//
看了dp解后恍然大悟,试着自己仿造了一个,慢慢来吧。
Code
#include<iostream>
using namespace std;
const double Suf=10000000.0;//界值要开得充分大,防止第一次找的时候没有记录数据
int main()
{
int N;
double L,C,T,VR,VT1,VT2,p[110],dp[110];;
while(cin>>L)
{
cin>>N>>C>>T>>VR>>VT1>>VT2;
for(int i=1;i<=N;i++) cin>>p[i];//完成输入
p[0]=0.0;p[N+1]=L;dp[0]=0.0;//将起点和终点也看作充电桩
for(int i=1;i<=N+1;i++)
{
double min=Suf;//注意该变量的作用域
for(int j=0;j<i;j++)
{
double len=p[i]-p[j],t=0.0;
if(len<=C) t=len/VT1;
else t=C/VT1+(len-C)/VT2;
if(j) t+=T;//除起点之外j要充电,其中j是到i之前最后一次充电。已上算的是从j不充电骑到i的时间
if((dp[j]+t)<min) min=dp[j]+t;//找用时最少的那个j
}
dp[i]=min;//获得到达dp[i]的最优解
}
if((L/VR)>dp[N+1]) cout<<"What a pity rabbit!"<<endl;
else cout<<"Good job,rabbit!"<<endl;
}
No. 2 过山车
Problem Description
二分图最大分配:一群人坐过山车,一男匹配一女,无匹配无法入座。
输入数据的第一行是三个整数K , M , N,分别表示可能的组合数目,女生的人数,男生的人数。0<K<=1000
1<=N 和M<=500.接下来的K行,每行有两个数,分别表示女生Ai愿意和男生Bj做partner。最后一个0结束输入。
对于每组数据,输出一个整数,表示可以坐上过山车的最多组合数。
Solution
S1: (匈牙利算法)先对第一个女孩寻找同伴,让女孩与其想组合的同伴组成一组,再对第二个女孩寻找同伴,如果第二个女孩的首选已经和第一个女孩配对,那就让第一个女孩另寻同伴,如果第一个女孩没有其他选择了,第二个女孩就应另做打算,如果第二个女孩也别无他选,那么第二个女孩就配对失败。以此类推,直至第n个女孩。
S2: (一种类似贪心的策略,未证明,自己想的)找到只有一种选择的人(单口)优先配对上,一步步把问题最后转化成剩下的都是有大于等于两种选择的人和没人要的人,把不饱和状态的图变成饱和的(算法应该有一定效果,但写出代码来tle了...
https://wenku.baidu.com/view/294e8cb91a37f111f1855b27.html(发现网上有类似的思路)
Detail
S1:每次循环开始时,都需要初始化变量
S2:单口的删除要做到完全,之后得到的才是饱和图
Summarize
这题这是个神坑...暴力求解完全看不出来咋做,递归的思路真的很巧妙了(ps:并不知道自己那错了...
Code
S1:
#include<iostream>
#include<string.h>
using namespace std;
const int N = 505;
bool map[N][N], flag[N];
int pre[N];
int n, m, num;
int find(int cur) //cur为当前女生
{
int i;
for (i = 1; i <= m; i++) //被匹配的男生
{
if (map[cur][i] && !flag[i]) //该男生未被匹配 并且被女生选中
{
flag[i] = true; //这次匹配中,标记该男生已匹配
if (pre[i] == -1 || find(pre[i]))//该男生没有被女生匹配 or 匹配了该男生的女生继续找其他男生 -> ok
{
pre[i] = cur; //男生[i]跟女生cur配对了
return 1;
}
}
}
return 0;
}
int main()
{
int i, girl, boy, sum;
while (cin>>num)
{
if(!num)return 0;
cin>>n>>m; //n是女生数量,m是男生数量
memset(map, false, sizeof(map));
memset(pre, -1, sizeof(pre));
for (i = 0; i < num; i++)
{
cin>>girl>>boy;
map[girl][boy] = true; //可以匹配
}
sum = 0;
for (i = 1; i <= n; i++) //女生去匹配男生
{
memset(flag, 0, sizeof(flag)); //每次重新标记0
sum += find(i);
}
cout<<sum<<endl;
}
return 0;
S2://tle了嘿嘿嘿
#include<iostream>
using namespace std;
int a[1010][1010];
main()
{
int K,W,M;
while(cin>>K)
{
if(K==0) break;
cin>>W>>M;
for(int i=1;i<=W;i++)
{
for(int j=1;j<=M;j++) a[i][j]=0;
}//应该是多余的
int w,m;
for(int i=1;i<=K;i++)
{
cin>>w>>m;
a[w][m]=1;
}//输入完成
int s=0,f=1;//计数
do
{
int z=0,t=-1,f=0;//判定是否为单点&标记擦除&判定是否不存在单点
for(int i=1;i<=W;i++)
{
for(int j=1;j<=M;j++)
{
z+=a[i][j];
if(z==1) t=j;
}//找到单点
if(z==1)
{
s++;
for(int q=1;q<=W;q++) a[q][t]=0;
f++;
}//擦除
}
}while(f!=0);//对女生操作
do
{
int z=0,t=-1,f=0;//判定是否为单点&标记擦除&判定是否不存在单点
for(int j=1;j<=M;j++)
{
for(int i=1;j<=W;i++)
{
z+=a[i][j];
if(z==1) t=i;
}//找到单点
if(z==1)
{
s++;
for(int q=1;q<=M;q++) a[t][q]=0;
f++;
}//擦除
}
}while(f!=0);//对男生操作
int sm=0,sw=0,st=0;
for(int i=1;i<=W;i++)
{
for(int j=1;j<=M;j++) sm+=a[i][j];
if(sm!=0) break;
}
for(int j=1;j<=M;j++)
{
for(int i=1;i<=W;i++) sw+=a[i][j];
if(sw!=0) break;
}
if(sm>sw) st=sw;
else st=sm;
cout<<s+st;
}
}
No. 3 解整数方程
Problem Description
x + y = n,x * y = m ? 找这样的整数x和y
输入数据为成对出现的整数n,m(-10000<n,m<10000),它们分别表示整数的和与积,如果两者都为0,则输入结束。对于每个n和m,输出“Yes”或者“No”,明确有还是没有这种整数。
Solution
在整数域上解一元二次方程
Detail
注意要保证有整数解
Code
#include<iostream>
using namespace std;
int turn(int,int);
int main()
{
int n;
while(cin>>n)
{
if(n==0) break;
if(turn(n,10)==turn(n,12)&&turn(n,12)==turn(n,16)) cout<<n<<" is a Sky Number."<<endl;
else cout<<n<<" is not a Sky Number."<<endl;
}
}
int turn(int n,int r)
{
int x[17],i,k=0,s=0;
do
{
k++;
i=n%r;
n/=r;
x[k]=i;
}while(n!=0);
for(int j=k;j>=1;--j) s+=x[j];
return s;
}
No. 4 判定素数
Problem Description
对于表达式n^2+n+41,当n在(x,y)范围内取整数值时(包括x,y)(-39<=x<y<=50),判定该表达式的值是否都为素数。输入数据有多组,每组占一行,由两个整数x,y组成,当x=0,y=0时,表示输入结束,该行不做处理。对于每个给定范围内的取值,如果表达式的值都为素数,则输出"OK",否则请输出“Sorry”,每组输出占一行。
Solution
写个判定素数的函数。
Detail
注意判定素数的函数中除数是从2开始的。
Code
#include<iostream>
#include<math.h>
using namespace std;
int prime(int x)
{
int t=sqrt(x);
if(x==1) return 0;
for(int i=2;i<=t;i++)
if(x%i==0) return 0;
return 1;
}
main()
{
int x,y;
while(cin>>x>>y)
{
if(x==0&&y==0) break;
int s=0,t;
for(int i=x;i<=y;i++)
{
t=prime(i*i+i+41);
if(t==1) s++;
}
if(s==(y-x+1)) cout<<"OK"<<endl;
else cout<<"Sorry"<<endl;
}
return 0;
No. 5 母牛个数
Problem Description
有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n(0<n<55)年的时候,共有多少头母牛? n=0表示输入数据的结束,不做处理。
Solution
本来内在递归关系不是很简单的,但列一下数据,发现规律很简单,递归一下。
Detail
无。
Code
#include<iostream>
using namespace std;
int cow(int n)
{
if(n<=3) return n;
else return cow(n-3)+cow(n-1);
}
main()
{
int n;
while(cin>>n)
{
if(n==0) break;
cout<<cow(n)<<endl;
}
}
No . 6 回文串
Problem Description
X判定是否为回文串。输入包含多个测试实例,输入数据的第一行是一个正整数n,表示测试实例的个数,后面紧跟着是n个字符串。如果一个字符串是回文串,则输出"yes",否则输出"no".
Solution
用string类内置的方法,和[]运算符。
Detail
注意string从0开始存储,还有这题不需要分奇偶。
Code
#include<iostream>
#include<string>
using namespace std;
int hui(string str)
{
int n=str.length();
for(int i=0;i<n/2;i++)
{
if(str[i]!=str[n-i-1]) return 0;
}
return 1;
}
main()
{
string str;
int n;
while(cin>>n)
{
int i,t;
for(i=1;i<=n;i++)
{
cin>>str;
t=hui(str);
if(t==1) cout<<"yes"<<endl;
if(t==0) cout<<"no"<<endl;
}
}
}
No . 7 进制转换
Problem Description
X输入一个十进制数N,将它转换成R进制数输出(2<=R<=16, R<>10)。为每个测试实例输出转换后的数,每个输出占一行。
Solution
模仿十进制,十进制中对10取模,R进制中对R取模。
Detail
如果R大于10,则对应的数字规则参考16进制(比如,10用A表示,等等)。用字符数组来存储这些字符。
Code
#include<iostream>
#include<stdlib.h>
using namespace std;
void turn(int,int);
char ch[6]={'A','B','C','D','E','F'};
main()
{
int n,r;
while(cin>>n>>r) turn(n,r);
}
void turn(int n,int r)
{
int x[17],i,j,k=0;
if(n<0) cout<<'-';
j=abs(n);
do
{
k++;
i=j%r;
j/=r;
x[k]=i;
}while(j!=0);
for(int h=k;h>=1;--h)
{
if(x[h]<10) cout<<x[h];
else cout<<ch[x[h]-10];
}
cout<<endl;
}
No. 8 求差集
Problem Description
每组输入数据占1行,每行数据的开始是2个整数n(0<=n<=100)和m(0<=m<=100),分别表示集合A和集合B的元素个数,然后紧跟着n+m个元素,前面n个元素属于集合A,其余的属于集合B. 每个元素为不超出int范围的整数,元素之间有一个空格隔开,如果n=0并且m=0表示输入的结束,不做处理。针对每组数据输出一行数据,表示A-B的结果,如果结果为空集合,则输出“NULL”,否则从小到大输出结果,为了简化问题,每个元素后面跟一个空格。
Solution
直接做(应该可以用set,但是我不会2333)
Detail
无。
Code
#include<iostream>
#include<algorithm>
using namespace std;
main()
{
int i,j,m,n,a[110],b[110],c[110];
while(cin>>n>>m)
{
if(n==0&&m==0) break;
for(i=1;i<=n;i++) cin>>a[i];
for(j=1;j<=m;j++) cin>>b[j];
int d=0;
for(i=1;i<=n;i++)
{
int s=0;
for(j=1;j<=m;j++)
if(a[i]!=b[j]) s++;
if(s==m)
{
c[d]=a[i];
d++;
}
}
if(d==0) cout<<"NULL"<<endl;
else
{
sort(c,c+d);
for(i=0;i<=d-1;i++) cout<<c[i]<<' ';
cout<<endl;
}
}
}
No. 9 A^B 后三位
Problem Description
求A^B的最后三位数表示的整数。输入数据包含多个测试实例,每个实例占一行,由两个正整数A和B组成(1<=A,B<=10000),如果A=0, B=0,则表示输入数据的结束,不做处理。对于每个测试实例,请输出A^B的最后三位表示的整数,每个输出占一行。
Solution
幂运算时只考虑后三位。
Detail
X注意这题关键时规避超内存。
Code
#include <iostream>
using namespace std;
int main()
{
int a,b;
while(cin>>a>>b)
{
if(a==0&&b==0) break;
int x=1;
for(int i=1;i<=b;i++)
{
x*=(a%1000);
x%=1000;
}
cout<<x<<endl;
}
No . 10 发工资
Problem Description
发工资,最少需要准备多少张人民币,才能在给每位老师发工资的时候都不用老师找零呢?(这里假设老师的工资都是正整数,单位元,人民币一共有100元、50元、10元、5元、2元和1元六种。)每个测试实例的第一行是一个整数n(n<100),表示老师的人数,然后是n个老师的工资。n=0表示输入的结束,不做处理。多组输入。
Solution
优先选面值大的,取模,累加。
Detail
无。
Code
#include<iostream>
using namespace std;
int num(int a)
{
int nu=0,b[6]={100,50,10,5,2,1};
for(int i=0;i<6;i++)
{
nu+=a/b[i];
if(a%b[i]==0) return nu;
a-=(a/b[i])*b[i];
}
}
main()
{
int n;
while(cin>>n)
{
if(n==0) break;
int i,a,s=0;
for(i=1;i<=n;i++)
{
cin>>a;
s+=num(a);
}
cout<<s<<endl;
}
}
No. 11 找规律
Problem Description
有如下方程:Ai = (Ai-1 + Ai+1)/2 - Ci (i = 1, 2, 3, .... n).
若给出A0, An+1, 和 C1, C2, .....Cn.
编程计算A1 = ? 输入包括多个测试实例。
对于每个实例,首先是一个正整数n,(n <= 3000); 然后是2个数a0, an+1.接下来的n行每行有一个数ci(i = 1, ....n);输入以文件结束符结束。对于每个测试实例,用一行输出所求得的a1(保留2位小数).
Solution
找规律。
Detail
无。
Code
#include <stdio.h>
#include<iostream>
using namespace std;
int main()
{
int n,i;
double a0,an,c[3010];
while(cin>>n)
{
cin>>a0>>an;
for(i =1;i<=n;i++) cin>>c[i];
double s,t=0;
for(i=1;i<=n;i++) t+=(n-i+1)*c[i];
s = a0*(0.5*n)+an/2.0-t;
s/=(0.5*(n+1));
printf("%.2lf\n",s);
}
return 0;
}
No . 12 掏空三角形
Problem Description
每行包含一个字符和一个整数n(0<n<41),不同的字符表示不同的花纹,整数n表示等腰三角形的高。显然其底边长为2n-1。如果遇到@字符,停。每个样板三角形之间应空上一行,三角形的中间为空。显然行末没有多余的空格。
Solution
思路很简单,但要注意细节。//这题不是自己独立做出来的以后要注意这种处理。
Detail
详见代码注释。主要注意用getchar()读取空格。
Code
#include <stdio.h>
#include <string.h>
int main()
{
char a;
int b,i,j,k=0;
while((a=getchar())!='@')//可换为:while(scanf("%c",&a)&&a!='@')
{
++k;
getchar();//这一句的作用是读取空格!
scanf("%d",&b);
getchar();//记住这一句不能省,否则会在第二次输入时a直接变成\n(换行符)!
if(k!=1)//这个格式很重要,这是在第一个结果的后面先不输出空行,
printf("\n");//只有在判断过第二个结果满足条件后,才输出空行,
//此空行依然在第二个结果之前,满足两个空三角形之间有空行的条件!
for(i=1;i<b;i++)
{
for(j=1;j<=b+(i-1);j++)
{
if(j==b-(i-1)||j==b+(i-1))
printf("%c",a);
else
printf(" ");
}
printf("\n");
}
for(i=0;i<2*b-1;i++)
printf("%c",a);
printf("\n");
}
return 0;
} }
printf("\n");
}
for(i=0;i<2*b-1;i++)
printf("%c",a);
printf("\n");
}
return 0;
}
No . 13 求夹角
Problem Description
在一个平面内有两个点,求两个点分别和原点的连线的夹角的大小。
注:夹角的范围[0,180],两个点不会在圆心出现。输入数据的第一行是一个数据T,表示有T组数据。每组数据有四个实数x1,y1,x2,y2分别表示两个点的坐标,这些实数的范围是[-10000,10000]。对于每组输入数据,输出夹角的大小精确到小数点后两位。
Solution
这题解法应该不止一种,我用的是余弦定理,调用了<math.h>库,最后还要注意角度和弧度的转化。对了,补充一点:似乎<math.h>库里的函数大部分要求形参的类型是double(包括abs()),所以这类题就不用费劲强转了。既然涉及了数据的精确输出,在这组题里遇到了需要四舍五入的情况,要记得printf()的精确就是采用的四舍五入。不需要再特意写一个函数了。
Detail
注意函数名别写错了(2333),还有定义PI的时候要注意精确度,否则会有较大误差。
Code
#include<iostream>
#include<stdio.h>
#include<math.h>
using namespace std;
main()
{
const double pi=3.1415926;//注意精确度
int n,i;
double x1,y1,x2,y2,a,b,c,d,r,s;
cin>>n;
for(i=1;i<=n;i++)
{
cin>>x1>>y1>>x2>>y2;
a=sqrt(x1*x1+y1*y1);
b=sqrt(x2*x2+y2*y2);
c=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
d=(a*a+b*b-c*c)/(2.0*a*b);
r=acos(d);
s=r*180/pi;
printf("%.2lf\n",s);
}
}
No . 14 电话号码
Problem Description
输入数据的第一行是一个N(N <= 200),表示有N个数据,接下来的N行每一行为一个11位的手机号码(无空格)。输出应包括N行,每行包括一个对应的短号(6+手机号的后5位,比如号码为13512345678的手机,对应的短号就是645678。),输出应与输入的顺序一致。
Solution
用c风格字符串。
Detail
无。
Code
#include<iostream>
using namespace std;
int main()
{
int n, i;
char a[12];
int b[2];
b[0] = 6;
cin >> n;
while (n)
{
cin >> a;
cout << b[0];
for (i = 6; i < 11; i++)
{
cout << a[i];
}
cout << endl;
n--;
}
return 0;
}
No . 15 蜜蜂
Problem Description
有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行。请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数。
其中,蜂房的结构如下所示。
输入数据的第一行是一个整数N,表示测试实例的个数,然后是N 行数据,每行包含两个整数a和b(0<a<b<50)。对于每个测试实例,请输出蜜蜂从蜂房a爬到蜂房b的可能路线数,每个实例的输出占一行。
Solution
这题看到的第一反应就是递归,然后发现tle了…然后发现直接正着推就行233…
Detail
注意规避tle...
#include<iostream>
long long int a[1000000],b[1000000];
using namespace std;
int main()
{
int n,t,x,y;
while(cin>>n)
{
a[2]=1,a[3]=2,a[4]=3,a[5]=5;
for(int i=1;i<=n;i++)
{
cin>>x>>y;
t=x-1;
y=y-t;
if(y>=6)
for(int j=6;j<=y;j++)
a[j]=a[j-1]+a[j-2];
cout<<a[y]<<endl;
}
}
}
总结
32道题,打完感觉还挺有意思的,虽然有一些不是独立完成的,但通过查资料也算是把轮子造出来了。该跳的坑都跳了,也学到了很多东西。刚刚接触编程和c++,还有很多不熟练的地方,掌握的知识也不多,要慢慢精进啦。