第一题:赢球票
某机构举办球票大奖赛。获奖选手有机会赢得若干张球票。
主持人拿出 N 张卡片(上面写着 1~N 的数字),打乱顺序,排成一个圆圈。
你可以从任意一张卡片开始顺时针数数: 1,2,3…
如果数到的数字刚好和卡片上的数字相同,则把该卡片收入囊中,从下一个卡片重新数数。
直到再无法收获任何卡片,游戏结束。囊中卡片数字的和就是赢得球票的张数。
比如:
卡片排列是:1 2 3
我们从1号卡开始数,就把1号卡拿走。再从2号卡开始,但数的数字无法与卡片对上,
很快数字越来越大,不可能再拿走卡片了。因此这次我们只赢得了1张球票。
还不算太坏!如果我们开始就傻傻地从2或3号卡片数起,那就一张卡片都拿不到了。
如果运气好,卡片排列是 2 1 3
那我们可以顺利拿到所有的卡片!
本题的目标就是:已知顺时针卡片序列。
随便你从哪里开始数,求最多能赢多少张球票(就是收入囊中的卡片数字之和)
输入数据:
第一行一个整数N(N<100),表示卡片数目
第二行 N 个整数,表示顺时针排列的卡片
输出数据:
一行,一个整数,表示最好情况下能赢得多少张球票
样例输入:
3
1 2 3
样例输出:
1
样例输入:
3
2 1 3
样例输出:
6
代码:
#include <vector>
#include <stack>
#include <string>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <queue>
#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define RI(a) scanf("%d",&a)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define mset2(a,val,n,m) for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=val;
#define FI freopen("in.txt","r",stdin)
#define FO freopen("out.txt","w",stdout)
using namespace std;
int main() {
int a[111],n,c,_max=0,sum=0,ans=0;
RI(n);
_for(i,0,n) {
RI(a[i]);
_max=max(a[i],_max);
}
//从第i张卡片开始顺时针数数
_for(i,0,n) {
c=1,sum=0;
vector<int> vec;
_for(j,0,n)vec.push_back(a[j]);
vector<int>::iterator pos=vec.begin()+i;//尝试从i开始数
while(vec.size()) {
if((*pos)==c) {
ans=max(ans,sum+=(*pos));//更新全局最大ans
pos = vec.erase(pos);//删除当前卡片,从下一个重新数(vec.earse会返回下一个迭代器)
c=1;//重新计数
} else {
pos++;
if(++c>_max)break;//当前数的数大于最大卡片
}
if(pos==vec.end())pos=vec.begin();
}
}
printf("%d\n",ans);
}
我学到的:
关于Vector的用法可以参考这篇博客:C++中STL库中的Vector容器
要注意关于Vector中erase的用法(最后传递类似于指针)
pos = vec.erase(pos); 删除当前卡片,从下一个重新数(vec.earse会返回下一个迭代器)
比赛前,将所有头文件写下来,还有特别好用的宏定义一定要学着用
第二题:冰雹数
题目大意:
给你一个数N,如果N为偶数则N=N/2,如果N为奇数N=N*3+1
循环下去问你N最大达到什么位置
比如N=9
9,28,14,7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1
可以看到,N=9的时候,这个“小冰雹”最高冲到了52这个高度
输入格式:
一个正整数N(N<1000000)
输出格式:
一个正整数,表示不大于N的数字,经过冰雹数变换过程中,最高冲到了多少
样例输入:
10
样例输出:
52
样例输入:
100
样例输出:
9232
代码:
#include <vector>
#include <stack>
#include <string>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <queue>
#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define RI(a) scanf("%d",&a)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define mset2(a,val,n,m) for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=val;
#define FI freopen("in.txt","r",stdin)
#define FO freopen("out.txt","w",stdout)
using namespace std;
typedef long long LL;
LL ans=0,N,x;
int main() {
cin>>N;
_for(i,1,N+1){
x=N;
while(x>1)
ans=max(ans,x=x%2?(x*3+1):(x/2));
}
cout<<ans<<endl;
}
第四题:立方体
小Hi在水平桌面上放置了一个立方体。初始时,上下左右前后6个面的编号依次是1、6、4、3、2、5
现在立方体经过若干次翻滚,每次翻滚是向前翻滚90度(F)、向后翻滚90度(B)、向左翻滚90度(L)、向右翻滚90度(R)之一
请你计算翻滚后上下左右前后6个面的编号依次是多少?
输入
一个由FBLR组成的序列。
长度不超过100。
输出
输出6行,每行一个整数。依次是上下左右前后6个面的编号。
样例输入
LB
样例输出
2
5
1
6
4
3
思路:
看字符串长度不回超过100,所以不用考虑效率,只需要写 向前F,向左L两个函数。然后其他的向后B就等于三次向前,R()=3L(),即把两个函数定义完就行了
代码:
#include <vector>
#include <stack>
#include <string>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <queue>
#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define RI(a) scanf("%d",&a)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define mset2(a,val,n,m) for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=val;
#define FI freopen("in.txt","r",stdin)
#define FO freopen("out.txt","w",stdout)
using namespace std;
typedef long long LL;
int a[6]={1,6,4,3,2,5};
char s[111];
void F(){
swap(a[0],a[5]);
swap(a[5],a[4]);
swap(a[5],a[1]);
}
void L(){
swap(a[1],a[3]);
swap(a[0],a[1]);
swap(a[1],a[2]);
}
int main() {
cin>>s;
int n=strlen(s);
_for(i,0,n){
if(s[i]=='L')L();
if(s[i]=='R'){L();L();L();}
if(s[i]=='F')F();
if(s[i]=='B'){F();F();F();}
}
_for(i,0,6)cout<<a[i]<<endl;
}
我学到的:
思路在于先创个代表上下左右前后的数组,然后直接对这个数组动手脚(swap)
注意L()和F()函数是在全局变量,且是不带参数的
对于字符串,可以再定义一个数组,然后就可以cin>>这样输进去(我才知道
第五题:提取用户名
在现在的各种互联网应用中,在一段文字中使用’@'字符来提起一名用户是流行的做法。
例如:
“@littleho submitted his code 30 times before he got passed the system test.”
其中littleho就是一个用户名。我们规定在一段文字中,’@'字符之后一段连续的、非空的大小写英文字母组成的字符串被视为提起的用户名。
给定一段文字,请你输出其中所有提到的用户名。
输入
一行文本,只包含大小写字母、标点符号和空格。长度不超过800。
输出
按文本中的顺序输出所有提到的用户名,之间用一个空格隔开。重复提到的相同用户名也重复输出。
样例输入
@abc:@@,@littleho’s code is so confusing. @abc.
样例输出
abc littleho abc
代码:
#include <vector>
#include <stack>
#include <string>
#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <queue>
#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define RI(a) scanf("%d",&a)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define mset2(a,val,n,m) for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=val;
#define FI freopen("in.txt","r",stdin)
#define FO freopen("out.txt","w",stdout)
using namespace std;
typedef long long LL;
char s[888],buf[888];
bool ischar(char c)
{
return (c>='A'&&c<='Z')||(c>='a'&&c<='z');
}
int main() {
cin.getline(s,888);
int n=strlen(s),flag=0;
_for(i,0,n) 循环结束的 i 会接着循环
{
if(s[i]=='@')
{
int l=++i; 使 i 加一,且从右至左赋值,故等号两边值相同
while(i<n&&ischar(s[i]))
buf[i-l]=s[i++]; 避免了使用两次 i++
if(strlen(buf)) 判断是否为空
{
i--; 因为再次循环 i++,避免与上面两次增加,故 i--
if(flag)cout<<" ";flag=1;
cout<<buf;
}
mset(buf,0,n); 清空数组
}
}
return 0;
}
我学到的:
主要思路:
创建两个数组,一个用于接收输入的一行字符串,一个用于输出
用 i 遍历整个字符串数组,从第一个开始判断是否使@,若是,则判断接下来的元素(是不是符合要求),若符合,则用输出数组接收,直到不符合的元素停止接收,开始输出(注意:第一次输出不用加空格,以后每次都要),最后清空数组,再遍历
cin.getline( )是用于获取一行字符串放入参数中的
第六题:算式最大值
给定:
1)N个正整数A1, A2, … AN;
2)P个加号+和Q个减号-; (P+Q=N-1)
3)K对括号()
请你使用全部整数、加减号和括号,组成一个合法的算式(A1~AN在算式中的顺序随意),使得算式的结果最大。
注意加减号只能作为二元运算符出现在算式中,不能作为正负号。
括号可以出现在算式最左和最右,例如(((1+2)))是合法的。
例如对于样例数据,(2-1)+3或3+(2-1)等都是结果最大的算式。
输入
第一行包含4个整数,N,P, Q和K。
第二行包含N个整数A1, A2, … AN。
2 ≤ N ≤ 100 P+Q+1=N 0 ≤ K ≤ 10
1 ≤ Ai ≤ 1000
输出
最大算式结果
样例输入
3 1 1 1
1 2 3
样例输出
4
分析
这道题一开始我只想到用全部大的数减去小的数,没有考虑到括号的问题,现在想想括号也很有用
比如这题有八个减号,若全部减小的则会得算出负数
现在有一个新思路:
如果存在括号就让减号尽力少,那么就可以这样4 -(1-2-3) = 4 -1+2+3,就变成一个减号了,显然只有一个减号时,就是最大和(前面已经讨论过减号不能全部消去),而且只需要一个括号就可以实现最大值,所以分为三种情况讨论:
代码:
#include <vector>
#include <stack>
#include <string>
#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <queue>
#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define RI(a) scanf("%d",&a)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define mset2(a,val,n,m) for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=val;
#define FI freopen("in.txt","r",stdin)
#define FO freopen("out.txt","w",stdout)
using namespace std;
typedef long long LL;
int n,p,q,k,a[111],sum=0,subsum=0;
int main()
{
cin>>n>>p>>q>>k;
_for(i,0,n)scanf("%d",&a[i]);
_for(i,0,n)sum+=a[i];
sort(a,a+n);
_for(i,0,q)subsum+=a[i];
if(q)sum-=k?(a[0]*2):subsum*2;
cout<<sum<<endl;
}
第七题:机器人移动
假设一个机器人在笛卡尔坐标系上。它从(X1, Y1)移动到了(X2, Y2),然后向右转90度,继续前进。
请你计算这个机器人继续前进过程中最先经过的整点是哪一个?
输入
四个整数X1, Y1, X2, Y2。
-1000000 ≤ X1, Y1, X2, Y2 ≤ 1000000 保证(X1, Y1)和(X2, Y2)是不同的点。
输出
两个整数X和Y代表最先经过的整点坐标。
样例输入
0 0 1 2
样例输出
3 1
分析:
一条向量(x,y)第一次经过的整点是坐标除以他们的最大公约数:(x/gcd(x,y),y/gcd(x,y))
代码:
#include <vector>
#include <stack>
#include <string>
#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <queue>
#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define RI(a) scanf("%d",&a)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define mset2(a,val,n,m) for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=val;
#define FI freopen("in.txt","r",stdin)
#define FO freopen("out.txt","w",stdout)
using namespace std;
typedef long long LL;
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
int main()
{
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
int dy=y2-y1,dx=x2-x1,gcdxy=gcd(abs(dx),abs(dy));
printf("%d %d\n",x2+dy/gcdxy,y2-dx/gcdxy);
}
我学到的:
最后之所以是用 x2+ 和用 y2- ,是因为题目问的是向右九十度后经过的整数点,所以其实是求法线的,法线的斜率于原来的斜率乘积是 -1 ,懂了吧?(好吧,还是不懂,以后再说吧…)
还有,又考初中数学?好吧,还是多刷题吧…
第八题:非法二进制数(未)
如果一个二进制数包含连续的两个1,我们就称这个二进制数是非法的。
小Hi想知道在所有 n 位二进制数(一共有2n个)中,非法二进制数有多少个。
例如对于 n = 3,有 011, 110, 111 三个非法二进制数。
由于结果可能很大,你只需要输出模109+7的余数。
输入
一个整数 n (1 ≤ n ≤ 100)。
输出
n 位非法二进制数的数目模10^9+7的余数。
样例输入
3
样例输出
3
分析:
像这一类计数问题一定要记住不遗漏,不重复。
这道题用动态规划计数,用d(i)表示i位非法二进制数的个数,i可以填0和1则分类,d0(i)表示以0结尾的非法数:
.分类计数加法原理:d(i)=d0(i)+d1(1);
1. i填0的时候:d0(i)=d(i-1); //因为0不能为前面做出贡献,填0就等于d(i-1)
2. i填1的时候,
(1)从不遗漏角度:首先 d1(i)肯定包含了d(i-1),d(i-1)就是看有没有遗漏,比如一个i-1位的数不是非法数但第i位填1就让它变成了非法数,比如1001填1后变成10011,所以这就是d(i-1)遗漏的补分,所以d1(i)就包含了d(i-1)和{i-1位以1为结尾的所有二进制数}
(2)从不重复角度:U1= d(i-1) 和U2={i-1位以1为结尾的所有二进制数}这两个集合 重复部分U1U2=d1(i-1),
综上i填1的时候,d1(i)= U1 + U2 - d1(i-1) = d0(i-1) + U2 = d0(i-1) + 2^(i-2);
递推式:
i>2时
d[0][i]=d[0][i-1]+d[1][i-1];
d[1][i]=d[0][i-1]+pow(2,i-2);
i=1时 d[0][1]=d[1][1]=0;
i=2时 d[0][2]=0;d[1][2]=1;
最终结果是 d[0][n]+d[1][n];
代码:
#include <vector>
#include <stack>
#include <string>
#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <queue>
#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define RI(a) scanf("%d",&a)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define mset2(a,val,n,m) for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=val;
#define FI freopen("in.txt","r",stdin)
#define FO freopen("out.txt","w",stdout)
using namespace std;
typedef long long LL;
LL d[2][111],mod=1000000007,pow2[111];
int main()
{
int n;
cin>>n;
pow2[0]=d[1][2]=1;
_for(i,1,n)pow2[i]=(pow2[i-1]*2)%mod;
_for(i,3,n+1){
d[0][i]=(d[0][i-1]+d[1][i-1])%mod;
d[1][i]=(d[0][i-1]+pow2[i-2])%mod;
}
cout<<(d[0][n]+d[1][n])%mod<<endl;
}
我学到的:
(回头看