T1.特殊的正方形
输入n,输出n行n列的由+
和.
组成的正方形,其中最外面一圈全是+
,第二圈全是.
,...,对于第i圈,如果i是奇数,那么全是+
,否则全是.
。
输入格式
一行,一个整数n。
输出格式
n行,为满足题目要求的正方形。注意不要有行末空格。
样例输入
10
样例输出
++++++++++
+........+
+.++++++.+
+.+....+.+
+.+.++.+.+
+.+.++.+.+
+.+....+.+
+.++++++.+
+........+
++++++++++
数据范围
对于100%100%的数据,保证2≤n≤100。
只要能知道字符所属的圈数就能很容易的知道它该是 ‘+’ 还是 ‘ . ’ ,设位置为(x,y),则圈数则是从x,y,n-x,n-y中取出的最小数
#include<iostream>
using namespace std;
int main(){
int n;
cin >> n;
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
if(min(min(i,n-i+1),min(j,n-j+1))%2)cout << '+';
else cout << '.';
}
cout << endl;
}
return 0;
}
T2.走楼梯2
楼梯有 n 阶,上楼可以一步上一阶,也可以一步上二阶。
但你不能连续三步都走两阶,计算走到第n阶共有多少种不同的走法。
输入格式
一行,一个数字,表示n。
输出格式
输出走楼梯的方式总数。
样例输入
6
样例输出
12
数据规模
对于100%100%的数据,保证n≤50。
简单的DP,不过比普通的走楼梯相比多了一个不能连续三次走两阶的条件,对于每一个台阶,将最后一次走一阶,最后一次走两阶而前一次为一阶,和最后连续走了两次两阶三种情况分开存储即可,代码如下
#include <iostream>
using namespace std;
int main(){
int n;
cin >> n;
long long ans[3][55] = {0};
ans[0][1] = 1;
ans[0][2] = 1;
ans[1][2] = 1;
for(int i = 3;i <= n+1;i++){
ans[0][i] = ans[0][i-1]+ans[1][i-1]+ans[2][i-1];
ans[1][i] = ans[0][i-2];
ans[2][i] = ans[1][i-2];
}
cout << ans[0][n+1];
return 0;
}
T3.走路
有一条很长的数轴,一开始你在0的位置。接下来你要走n步,第i步你可以往右走ai或者bi。
问n步之后,0到m的每个位置,能不能走到?
输入格式
第一行,两个整数n,m。
接下来n行,每行两个整数ai,bi。
输出格式
一行,一共m+1个数,每个数都是0
或1
表示能否走到,数字之间不用空格隔开。
输入样例
3 10
1 2
2 6
3 3
输出样例
00000011001
数据规模
对于所有数据,保证1≤n≤100,1≤m≤105,1≤ai,bi≤1000。
一个简单的记忆化DP,一步一步模拟就可以
#include <bits/stdc++.h>
using namespace std;
int main() {
int n,m;
scanf("%d %d",&n,&m);
int steplength[2][105] = {0};
bool road[100005] = {0};
road[0] = true;
for(int i = 0;i < n;i++){
scanf("%d %d",&steplength[0][i],&steplength[1][i]);
}
for(int i = 0;i < n;i++){
for(int j = m;j >= 0;j--){
if(road[j]){
if(j+steplength[0][i]<=m)road[j+steplength[0][i]] = true;
if(j+steplength[1][i]<=m)road[j+steplength[1][i]] = true;
road[j] = false;
}
}
}
for(int i = 0;i <= m;i++){
printf("%d",road[i]);
}
return 0;
}
T4.简单分数统计
N 个好朋友在codeforces上参加一场包含 M 个题目的比赛, 比赛期间codeforces网站一共有 k 次提交。
已知每个题目的分数,
但是由于他们只能查到在比赛期间codeforces总共的提交记录(其他用户提交的其他题目记录也包含在内, 即存在不属于该场比赛的题目),
所以想请你编写一个程序算出他们每个人的分数。
输入格式
第一行三个整数 N, M, K 分别表示好朋友的个数, 题目的个数, 和提交的总次数(其中0<N,M,K<=200)。
接下来 N 行 第 i 行输入为第 i 个人的id,
接下来 M 行 第 j 行输入为第 j 个题目的名称和分数,
接下来 K 行 第 k 行输入为第 k 次提交的提交者id, 题目名称和结果("WA" 或 "AC", 如果"AC"代表通过这个题目, 提交者获得对应分数)。
注: 题目名称和id均为仅包含英文字母和数字的字符串, 题目分数为小于等于 1e61�6 的正整数. 每一行的多个输入之间用空格隔开。
所有输入的字符串长度 length 满足 0<length≤500。
所有用户id和题目名称不存在重名, 用户AC了某个题之后之后不会再重复提交该题, 好朋友们只会提交属于比赛的题目。
输出格式
输出 N 行, 第 i 行输出第 i 个人的名字和对应分数 (名字和分数用空格隔开)。
样例输入
2 2 4
GabrielPessoa
beza
metebronca 100
geometry 200
beza metebronca AC
ffern numbertheory AC
GabrielPessoa geometry WA
beza geometry AC
样例输出
GabrielPessoa 0
beza 300
样例解释
beza 过了 metebronca和geometry 拿到 300300 分。
GabrielPessos 没有过题, 所以是 00 分。
还有一些其他选手提交的其他题目忽略不计。
需要记下朋友的名字及他们对应的分数,和每道题的名字及AC过后的分数,这两个用map就可以很方便的解决,之后就是简单的模拟了
#include <iostream>
#include <map>
using namespace std;
int main(){
int n,m,k;
string ts,tss;
int ti;
map<string,int>qscore;
string fname[205];
int fscore[205] = {0};
cin >> n >> m >> k;
for(int i = 0;i < n;i++){
cin >> fname[i];
}
for(int i = 0;i < m;i++){
cin >> ts >> ti;
qscore[ts] = ti;
}
for(int i = 0;i < k;i++){
cin >> ts;
ti = n;
for(int i = 0;i < n;i++){
if(fname[i]==ts){
ti = i;
}
}
cin >> ts >> tss;
if(ti==n)continue;
else{
if(tss=="AC"){
fscore[ti]+=qscore[ts];
}
}
}
for(int i = 0;i < n;i++){
cout << fname[i] << ' ' << fscore[i] << endl;
}
return 0;
}
T5.Alice的德州扑克
德州扑克是目前世界上最流行的扑克游戏,全世界有众多相关的比赛,例如是 WSOP,WPT,EPT等,也让这款游戏的玩法变得层出不穷,丰富多变。 不要被简单的游戏规则而误导,复杂多变的比赛状况,让这款游戏在高水平的竞技中会变得非常复杂,这也让人们为德州扑克给出了这样一句评价 ”用一刻就能学会,但要用一生才能掌握” 。
现在我们并不在乎游戏规则是什么,因为 Alice 是一个德州扑克高手,他对于德州扑克的规则烂熟于心,不过他每次都记不得牌型的大小关系,他知道你是一个编程高手,所以他想让你帮他写一个程序:输入五张牌的大小和花色,输出这五张牌能组成的最大牌型.你能帮帮他吗?
为了降低你的编程难度,我们规定:
-
输入的牌都是来源于同一副扑克牌
-
输入的牌的点数都是非递减的
-
所有花色没有大小之分
下面给出各牌型,(从大到小)
-
皇家同花顺(ROYAL FLUSH):五张顺连的牌(点数连续单调递增),且最大的一张牌是A(Ace),并且五张牌的花色相同
-
同花顺(STRAIGHT FLUSH):五张顺连的牌(点数连续单调递增),不规定最大的一张牌是A(Ace),并且五张牌的花色相同
-
四条(FOUR OF A KIND):至少四张牌的点数相同
-
葫芦(FULL HOUSE):至少三张牌的点数相同,并且除此之外还有两张牌的点数相同
-
同花(FLUSH):五张牌的花色都相同
-
顺子(STRAIGHT):五张顺连的牌(点数连续单调递增),不要求五张牌的花色相同
-
特别注意:由于 Alice 是个谨慎的人,所以比 三条(THREE OF A KIND) (包括三条) 小的牌型 Alice 不在乎他们的大小关系,你只需要告诉 Alice 弃牌就行
输入格式
输入两行,每行五个数字,第一行的第 i� 个字符表示第 i� 张扑克的点数,
第二行的第 i� 个数字表示第 i� 张扑克花色。(保证输入的牌的点数是非递减的,且所有输入均合法)。
点数和对应输入的数字:
- 2−102−10 对应 2 - 10
- J(Jack) 对应 11
- Q(Queen) 对应 12
- K(King) 对应 13
- A(Ace) 对应 14
花色和对应输入的数字:
- 黑桃 (Spades) 对应 1
- 方片 (Diamonds) 对应 2
- 红桃 (Hearts) 对应 3
- 梅花 (Clubs) 对应 4
输出格式
输出这五张牌能组成的最大牌型。
- 如果最大是皇家同花顺输出 "ROYAL FLUSH"
- 如果最大是同花顺输出 "STRAIGHT FLUSH"
- 如果最大是四条输出 "FOUR OF A KIND"
- 如果最大是葫芦输出 "FULL HOUSE"
- 如果最大是同花输出 "FLUSH"
- 如果最大是顺子输出 "STRAIGHT"
- 如果最大的牌型小于等于三条输出"FOLD",劝 Alice 弃牌
- 输出不包括引号
样例输入1
10 11 12 13 14
1 1 1 1 1
样例输出1
ROYAL FLUSH
样例输入2
10 11 12 13 14
1 2 1 3 4
样例输出2
STRAIGHT
样例输入3
6 6 6 7 7
1 2 3 1 3
样例输出3
FULL HOUSE
样例输入4
3 3 6 6 9
1 2 1 2 1
样例输出4
FOLD
这个嘛就是一大堆的条件去判断了,难倒是不难,就是需要写一堆条件来判断是哪种情况,直接贴上AC代码吧
#include <iostream>
using namespace std;
int main(){
bool t4=false,t3=false,t2=false;
int n[15]={0};
int s[5];
int h[5];
for(int i = 0;i < 5;i++){
cin >> s[i];
n[s[i]]++;
}
for(int i = 0;i < 5;i++){
cin >> h[i];
}
for(int i = 0;i < 15;i++){
if(n[i]==2)t2=true;
if(n[i]==3)t3=true;
if(n[i]==4)t4=true;
}
if((s[4]==14)&&(s[4]-s[3]==1)&&(s[3]-s[2]==1)&&(s[2]-s[1]==1)&&(s[1]-s[0]==1)&&(h[0]==h[1])&&(h[1]==h[2])&&(h[2]==h[3])&&(h[3]==h[4])){
cout << "ROYAL FLUSH" << endl;
}
else if((s[4]-s[3]==1)&&(s[3]-s[2]==1)&&(s[2]-s[1]==1)&&(s[1]-s[0]==1)&&(h[0]==h[1])&&(h[1]==h[2])&&(h[2]==h[3])&&(h[3]==h[4])){
cout << "STRAIGHT FLUSH" << endl;
}
else if(t4){
cout << "FOUR OF A KIND" << endl;
}
else if(t3&&t2){
cout << "FULL HOUSE" << endl;
}
else if((h[0]==h[1])&&(h[1]==h[2])&&(h[2]==h[3])&&(h[3]==h[4])){
cout << "FLUSH" << endl;
}
else if((s[4]-s[3]==1)&&(s[3]-s[2]==1)&&(s[2]-s[1]==1)&&(s[1]-s[0]==1)){
cout << "STRAIGHT" << endl;
}
else{
cout << "FOLD" << endl;
}
return 0;
}
T6.订单编号
小缘开了一家公司,生意很好,每天都会收到很多订单,自动交易系统会自动给这些订单生成没有重复的订单编号。但是有一天,系统出现了未知的错误,导致当天的订单编号可能有重复的,这可把小缘急坏了。你可以帮助小缘按照规则给这些订单重新编号吗?
按照时间先后顺序给出 N 个正整数作为原订单编号,你需要按照规则依次赋予这些订单新的编号,对于任意一个订单,要找到大于等于其原订单编号且未被使用过的(没有被之前的订单作为新的订单编号)的最小整数,作为它的新订单编号。
例如: 原订单编号依次为1 2 3 1,则新订单编号应该为1 2 3 4 (前3个订单的原订单编号都没有使用过,所以用其原订单编号即可,对于第四个订单,原订单编号为1,而1, 2, 3都已经被使用过,所以新订单编号为4)。
输入格式
第一行输入一个整数 N (1≤N≤5×105)。
第二行输入 N 个数 ai (1≤ai≤109) 作为原订单编号。
输出格式
输出一行,包含 N 个整数为新的订单编号。
样例输入1
6
2 3 4 1 1 1
样例输出1
2 3 4 1 5 6
样例输入2
3
1000000000 1000000000 1000000000
样例输出2
1000000000 1000000001 1000000002
样例输入3
6
4 5 1 2 1 1
样例输出3
4 5 1 2 3 6
这道题需要给订单编号重复了的订单向后寻找最小的没有被使用过的订单编号,很明显直接使用数组肯定会TLE,所以我们使用一个set容器,存下所有没有被使用过的订单编号,以区间的形式存储进去,在接收一个新的订单编号时,若存在某个集合包括了这个订单编号,那么这个编号即没有被使用过,设这个区间为(x,y),订单编号为n(x<n<y),将这个区间移除并且加入(x,n-1),(n+1,y)两个区间即可实现操作,若找不到,即这个编号被使用过,向后寻找最小的区间,将这个区间的去除最小的那个数即可
#include <iostream>
#include <set>
using namespace std;
set<pair<int,int> >st;
void sinsert(int a,int b){
if(a<b)return;
st.insert({a,b});
}
int main(){
int n,temp;
cin >> n;
st.insert({2e9,0});
while(n--){
scanf("%d",&temp);
set<pair<int,int> >::iterator it = st.lower_bound({temp,0});
if(it->second < temp){
printf("%d ",temp);
sinsert(temp-1,it->second);
sinsert(it->first,temp+1);
st.erase(it);
}
else {
printf("%d ",it->second);
sinsert(it->first,it->second+1);
st.erase(it);
}
}
return 0;
}
T7.饿饿 饭饭
有n个同学正在排队打饭,第i个同学排在从前往后第i个位置。但是这天食堂内只有一个食堂阿姨,为了使同学们都能尽快的吃上饭,每一个同学在打完一份饭之后就会排在队伍的末尾先吃着打到的饭,我们知道第i个同学的饭量为ai,也就是说第i个同学要吃ai份饭才能吃饱,当一位同学吃饱后,他就会立刻离开食堂,不会排在队伍的末尾。食堂阿姨想知道,在打完k份饭之后,队伍的样子是怎样的,但是食堂阿姨数学不太好,想让你帮忙想想办法。
输入格式
第一行给出两个整数n,k。
第二行给出n个整数a1,a2,......an。
输出格式
如果食堂阿姨打饭数少于k,请输出"-1"。
否则按照队伍顺序输出每一个同学的编号。
样例输入1
3 3
1 2 1
样例输出1
2
样例输入2
4 10
3 3 2 1
样例输出2
-1
样例输入3
7 10
1 3 3 1 2 3 1
样例输出3
6 2 3
数据规模
数据保证1≤n≤10^5, 0≤k≤10^14, 1≤ai≤10^9。
一个模拟题了,如果所有人都早就吃饱了那就没什么可说的了,如果是打一半的话首先二分答案求出能打完多少个整轮,最后一轮直接模拟情况,吃饱了直接去掉没吃饱放到队尾,正好打完k份之后把队列打印出来即可
#include <iostream>
#include <deque>
using namespace std;
deque<int>de;
long long sum = 0;
long long n,k;
int a[100005] = {0};
bool check(int l){
sum = 0;
for(int i = 1;i <= n;i++){
sum += min(a[i],l);
}
if(sum <= k)return true;
return false;
}
int main(){
long long cnt = 0;
cin >> n >> k;
for(int i = 1;i <= n;i++){
cin >> a[i];
cnt+=a[i];
}
if(cnt < k){
cout << -1 << endl;
return 0;
}
int l = 0;
int r = 1e9+5;
while(l<=r){
int mid = (l+r)/2;
if(check(mid))l = mid + 1;
else r = mid - 1;
}
check(r);
k -= sum;
for(int i = 1;i <= n;i++){
if(a[i]<=r)continue;
else de.push_back(i);
}
while(k--){
if(a[de.front()] == r+1)de.pop_front();
else{
de.push_back(de.front());
de.pop_front();
}
}
for(deque<int>::iterator it = de.begin();it != de.end();it++){
printf("%d ",*it);
}
return 0;
}
T8.任务分配
你有n个任务,其中第i个任务,在si开始,ei时刻结束,如果做这个任务,你能获得wi的收益。
但是你在一个时刻只能做一个任务,问选择哪些任务,能让你的收益尽量大。
注意:你在上一个任务结束后马上开始下一个任务是可以的。
输入格式
第一行一个整数n。
接下来n行,每行三个整数si,ei,wi。
输出格式
一个数,表示答案。
样例输入
3
1 3 100
2 4 199
3 5 100
样例输出
200
数据规模
对于所有数据,保证1≤n≤10^3,1≤si<ei≤10^3,1≤wi≤10^5。
很明显一个背包问题,直接DP就可以
#include <iostream>
using namespace std;
#define N 1005
int main(){
int f[N] = {0};
int s[N],e[N],w[N];
int n;
cin >> n;
for(int i = 0;i < n;i++){
cin >> s[i] >> e[i] >> w[i];
}
for(int i = 1;i <= 1000;i++){
f[i] = f[i-1];
for(int j = 0;j < n;j++){
if(e[j]==i){
f[i] = max(f[i],f[s[j]]+w[j]);
}
}
}
cout << f[1000];
return 0;
}