第二期训练题目来源:Daimayuan Online Judge, div2
link
101特殊的正方形
题目描述
输入n,输出n行n列的由+
和.
组成的正方形,其中最外面一圈全是+
,第二圈全是.
,…,对于第i圈,如果i是奇数,那么全是+
,否则全是.
。
输入格式
一行,一个整数n。
输出格式
n行,为满足题目要求的正方形。注意不要有行末空格。
样例输入
10
样例输出
++++++++++
+........+
+.++++++.+
+.+....+.+
+.+.++.+.+
+.+.++.+.+
+.+....+.+
+.++++++.+
+........+
++++++++++
数据范围
对于100%的数据,保证2≤n≤100。
思路
创建n×n的数组,一圈一圈赋值
代码
#include <bits/stdc++.h>
using namespace std;
int n;
char mapp[105][105];
int main(){
cin >> n;
for(int i = 1; i <= n/2+1; i++){
if(i%2!=0){
for(int j = i; j <= n-i+1; j++) mapp[i][j]='+';
for(int j = i; j <= n-i+1; j++) mapp[j][n-i+1]='+';
for(int j = i; j <= n-i+1; j++) mapp[n-i+1][j]='+';
for(int j = i; j <= n-i+1; j++) mapp[j][i]='+';
}
else{
for(int j = i; j <= n-i+1; j++) mapp[i][j]='.';
for(int j = i; j <= n-i+1; j++) mapp[j][n-i+1]='.';
for(int j = i; j <= n-i+1; j++) mapp[n-i+1][j]='.';
for(int j = i; j <= n-i+1; j++) mapp[j][i]='.';
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cout << mapp[i][j];
}
cout << endl;
}
}
102走楼梯2
题目描述
楼梯有 n 阶,上楼可以一步上一阶,也可以一步上二阶。
但你不能连续三步都走两阶,计算走到第n阶共有多少种不同的走法。
输入格式
一行,一个数字,表示n。
输出格式
输出走楼梯的方式总数。
样例输入
6
样例输出
12
数据规模
对于100%的数据,保证n≤50。
思路
状态转移:
(1)前5个:第i阶为第i-1阶与第i-2阶的和
(2)从第6阶开始:第i阶可以从第i-1阶迈一步;可以从第i-2阶迈两步上来,
但是第i-2阶只能从i-3阶迈一步上来或者从i-5阶迈到i-4阶(一步)再迈到i-2阶(两步)
也即写为方程:
//状态转移:dp[i]=dp[i-1]+dp[i-2]; 前五个
//dp[i]=dp[i-1]+dp[i-3]+dp[i-5]; 之后的
代码
//状态转移:dp[i]=dp[i-1]+dp[i-2]; 前五个
//dp[i]=dp[i-1]+dp[i-3]+dp[i-5]; 之后的
#include <bits/stdc++.h>
using namespace std;
int n;
long long dp[55]; //int超范围
int main(){
cin >> n;
dp[0]=1;
dp[1]=1;
for(int i = 2; i <= n; i++){
if(i<6)
dp[i]=dp[i-1]+dp[i-2];
else{
dp[i]=dp[i-1]+dp[i-3]+dp[i-5];
}
}
cout << dp[n];
}
注意,n=50时超出int范围,故用long long
103走路
题目描述
有一条很长的数轴,一开始你在0的位置。接下来你要走n步,第i步你可以往右走 a i a_i ai 或者 b i b_i bi。
问n步之后,0到m的每个位置,能不能走到?
输入格式
第一行,两个整数n,m。
接下来n行,每行两个整数 a i , b i a_i,b_i ai,bi。
输出格式
一行,一共m+1个数,每个数都是0或1表示能否走到,数字之间不用空格隔开。
输入样例
3 10
1 2
2 6
3 3
输出样例
00000011001
数据规模
对于所有数据,保证 1 ≤ n ≤ 100 , 1 ≤ m ≤ 105 , 1 ≤ a i , b i ≤ 1000 1≤n≤100,1≤m≤105,1≤a_i,b_i≤1000 1≤n≤100,1≤m≤105,1≤ai,bi≤1000。
思路
简单来说就是看n步之后会落在哪里,可以dfs,但我怕超时,用了滚动数组
c[步数][位置]=1or0,主要看前一个减去本次需要走的步数时有没有走过
代码
#include <bits/stdc++.h>
using namespace std;
const int maxx = 1e5+5;
int n, m;
int a[maxx], b[maxx],c[105][maxx];
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i] >> b[i];
memset(c,0,sizeof(c));
c[0][0]=1;
for(int i = 1; i <= n; i++){
for(int j = a[i]; j <= m; j++){
// cout << c[i-1][j-a[i]];
if(c[i-1][j-a[i]]) c[i][j]=1;
}
for(int j = b[i]; j <= m; j++){
if(c[i-1][j-b[i]]) c[i][j]=1;
}
}
for(int i = 0; i <= m; i++){
cout << c[n][i];
}
}
104简单分数统计
题目描述
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均为仅包含英文字母和数字的字符串, 题目分数为小于等于 1e6 的正整数. 每一行的多个输入之间用空格隔开。
所有输入的字符串长度 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 拿到 300 分。
GabrielPessos 没有过题, 所以是 0 分。
还有一些其他选手提交的其他题目忽略不计。
思路
判断k个提交里的人名、题目名、是否ac
代码
#include <bits/stdc++.h>
using namespace std;
const int Max1 = 205;
const int Max2 = 1e6+5;
int n, m, k;
string id[Max1], name[Max1];
int score[Max1], manfen[Max1];
string a,b,c;
int main(){
cin >> n >> m >> k;
memset(score,0,sizeof(score));
for(int i = 1; i <= n; i++) cin >> id[i];
for(int i = 1; i <= m; i++) cin >> name[i] >> manfen[i];
for(int i = 1; i <= k; i++){
cin >> a >> b >> c;
for(int j = 1; j <= n; j++){
if(a==id[j]){
if(c=="AC"){
for(int k = 1; k <= m; k++){
if(b==name[k]) score[j]+=manfen[k];
}
}
}
}
}
for(int i = 1; i <= n; i++){
cout << id[i] << " " << score[i] << endl;
}
}
105Alice的德州扑克
题目描述
德州扑克是目前世界上最流行的扑克游戏,全世界有众多相关的比赛,例如是 WSOP,WPT,EPT等,也让这款游戏的玩法变得层出不穷,丰富多变。 不要被简单的游戏规则而误导,复杂多变的比赛状况,让这款游戏在高水平的竞技中会变得非常复杂,这也让人们为德州扑克给出了这样一句评价 ”用一刻就能学会,但要用一生才能掌握” 。
现在我们并不在乎游戏规则是什么,因为 Alice 是一个德州扑克高手,他对于德州扑克的规则烂熟于心,不过他每次都记不得牌型的大小关系,他知道你是一个编程高手,所以他想让你帮他写一个程序:输入五张牌的大小和花色,输出这五张牌能组成的最大牌型.你能帮帮他吗?
为了降低你的编程难度,我们规定:
1. 输入的牌都是来源于同一副扑克牌
2. 输入的牌的点数都是非递减的
3. 所有花色没有大小之分
下面给出各牌型,(从大到小)
-
皇家同花顺(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−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
思路
先判断是否成顺、成花,再加强条件,成条单独判断,其余的都是弃牌情况
代码
#include <bits/stdc++.h>
using namespace std;
struct p{
int rand;
int hua;
bool operator<(p a){
return rand <= a.rand;
}
}pai[5];
string ans[7]={"ROYAL FLUSH", "STRAIGHT FLUSH", "FOUR OF A KIND", "FULL HOUSE", "FLUSH", "STRAIGHT", "FOLD"};
int ind=7;
int t(p d[]){
if(d[0].rand==d[1].rand && d[0].rand==d[2].rand && d[0].rand==d[3].rand && d[0].rand==d[4].rand) return 4;
else if(d[0].rand==d[1].rand && d[0].rand==d[2].rand && d[0].rand==d[3].rand) return 4;
else if(d[1].rand==d[2].rand && d[1].rand==d[3].rand && d[1].rand==d[4].rand) return 4;
else if(d[0].rand==d[1].rand && d[0].rand==d[2].rand && d[3].rand==d[4].rand) return 3;
else if(d[0].rand==d[1].rand && d[2].rand==d[3].rand && d[3].rand==d[4].rand) return 3;
else return 9;
}
int main(){
for(int i = 0; i < 5; i++) cin >> pai[i].rand;
for(int i = 0; i < 5; i++) cin >> pai[i].hua;
int nowr=pai[0].rand, nowh=pai[0].hua;
//成顺、成花
int flag1=1, flag2=1, flag3=1;
for(int i = 1; i < 5; i++){
if(pai[i].rand==nowr+1) {flag1++;nowr=pai[i].rand;}
if(pai[i].hua==nowh) {flag2++;}
}
if(flag1==5){
if(flag2==5 && pai[4].rand==14) ind=1;
else if(flag2==5) ind = 2;
else ind = 6;
}
else if(flag2==5) ind = 5;
else if(t(pai)==4) ind=3;
else if(t(pai)==3) ind=4;
else ind = 7;
cout << ans[ind-1];
}
201任务分配
题目描述
你有n个任务,其中第i个任务,在 s i s_i si开始, e i e_i ei时刻结束,如果做这个任务,你能获得 w i w_i wi的收益。
但是你在一个时刻只能做一个任务,问选择哪些任务,能让你的收益尽量大。
注意:你在上一个任务结束后马上开始下一个任务是可以的。
输入格式
第一行一个整数n。
接下来n行,每行三个整数 s i , e i , w i s_i,e_i,w_i si,ei,wi。
输出格式
一个数,表示答案。
样例输入
3
1 3 100
2 4 199
3 5 100
样例输出
200
思路
贪心,再时间t内选取价值最大的任务选择
在时间i内,如何选择以时间i结束的任务的方程dp[i]=max(dp[i],dp[ren[j].s]+ren[j].w)
代码
#include <bits/stdc++.h>
using namespace std;
const int Max = 1e3+5;
int n;
struct r{
int s,e,w;
bool operator <(r q){
return e <= q.e || (e==q.e && s <= q.s);
}
}ren[Max];
int dp[Max];
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> ren[i].s >> ren[i].e >> ren[i].w;
}
sort(ren+1,ren+n+1);
int maxe = ren[n].e;
int now=0;
dp[0]=0;
for(int i = 1; i <= maxe; i++){
dp[i]=dp[i-1];
for(int j = 1; j <= n; j++){
if(i==ren[j].e){
dp[i]=max(dp[i],dp[ren[j].s]+ren[j].w);
}
}
}
cout << dp[maxe];
}
**注意:**要按结束时间的早晚排好序
202路径计数
题目描述
有一个n×n的网格,有些格子是可以通行的,有些格子是障碍。
一开始你在左上角的位置,你可以每一步往下或者往右走,问有多少种走到右下角的方案。
由于答案很大,输出对109+7取模的结果。
输入格式
第一行一个正整数n。
接下来n行,每行n个正整数,1表示可以通行,0表示不能通行。
输出格式
一个整数,表示答案。
样例输入
3
1 1 1
1 0 1
1 1 1
样例输出
2
思路
倒着想,到达(i, j)点的方法有哪些
(1)如果这个点上左都通,则dp[i][j]=(dp[i-1][j]+dp[i][j-1])
(2)如果只有一个通,则dp[i][j]=dp[i-1][j]或dp[i][j]=dp[i][j-1]
(3)如果都不通,则 dp[i][j]=0
所有点都遍历一边即可知道答案
代码
#include <bits/stdc++.h>
using namespace std;
const long long mod = 1e9+7;
int n;
int mapp[105][105];
long long dp[105][105];
long long ans;
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> mapp[i][j];
}
}
dp[1][1]=1;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(mapp[i-1][j]==1 && mapp[i][j-1])
dp[i][j]=(dp[i-1][j]+dp[i][j-1])%mod;
else if(mapp[i-1][j]==1)
dp[i][j]=dp[i-1][j]%mod;
else if(mapp[i][j-1]==1)
dp[i][j]=dp[i][j-1]%mod;
else
dp[i][j]=0;
dp[1][1]=1;
}
}
cout << dp[n][n];
}
**注意:**dp[1][1]=1
203最大和上升子序列
题目描述
给定一个长度为 n 的数组 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an,问其中的和最大的上升子序列。也就是说,我们要找到数组 p 1 , p 2 , … , p m p_1,p_2,…,p_m p1,p2,…,pm,满足 1 ≤ p 1 < p 2 < ⋯ < p m ≤ n 1≤p_1<p_2<⋯<p_m≤n 1≤p1<p2<⋯<pm≤n 并且 a p 1 < a p 2 < ⋯ < a p m a_{p_1}<a_{p_2}<⋯<a_{p_m} ap1<ap2<⋯<apm,使得 a p 1 + a p 2 + ⋯ + a p m a_{p_1}+a_{p_2}+⋯+a_{p_m} ap1+ap2+⋯+apm最大。
输入格式
第一行一个数字 n。
接下来一行 n 个整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an。
输出格式
一个整数,表示答案。
样例输入
6
3 7 4 2 6 8
样例输出
21
数据规模
所有数据保证 1 ≤ n ≤ 1000 , 1 ≤ a i ≤ 1 0 5 1≤n≤1000,1≤a_i≤10^5 1≤n≤1000,1≤ai≤105。
思路
遍历一遍,算出对于每个 a i a_i ai的最大上升序列和,对这n个和取最大即可
代码
#include <bits/stdc++.h>
using namespace std;
int n;
int a[1005];
long long dp[1005];
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
for(int i = 1; i <= n; i++){
dp[i]=a[i];
for(int j = i-1; j >= 0; j--){
if(a[j]<a[i])
dp[i]=max(dp[i],dp[j]+a[i]);
}
}
long long x=0;
for(int i= 1; i <= n; i++){
// cout << dp[i] << " ";
x = max(x,dp[i]);
}
cout << x ;
}