题目描述:计算1-2020中出现了多少次2,注意不是多少个数字出现2。
题解:直接写,送分题
C++ 代码:
#include
using namespace std;
int res = 0;
void count(int x)
{
while(x)
{
if(x % 10 == 2) res ++;
x /= 10;
}
}
int main()
{
for(int i = 0;i <= 2020 ; i ++ )
count(i);
cout << res << endl;
return 0;
}
// 624
题目描述:求多少个分数使得分子和分母的最大公约数为1,且同时分子和分母均在1-2020之间。
题解: 数论 gcd,补充算法:在algorithm包下有一个函数__gcd(int i,int j)
牛啊,以后再也不傻傻的去写gcd函数了,啦啦啦啦
这里也写一下gcd函数,毕竟比较经典的辗转相减法
int gcd(int a, int b)
{
if(!b) return a;
return gcd(b, a % b);
}
C++ 代码:
#include
#include
using namespace std;
int main()
{
int res = 0;
for(int i = 1; i <= 2020; i ++ )
for(int j = 1; j <= 2020; j ++ )
if(__gcd(i, j) == 1) res ++;
cout << res << endl;
return 0;
}
//2481215
题目描述:
如下图所示,小明用从 1 开始的正整数“蛇形”填充无限大的矩阵。
1 2 6 7 15 :::
3 5 8 14 :::
4 9 13 :::
10 12 :::
11 :::
:::
容易看出矩阵第二行第二列中的数是 5。请你计算矩阵中第 20 行第 20 列的数是多少 ?
题解: 这题我看大家都是去模拟题目的意思,打印出蛇形矩阵,其实不然,他问的是第20行第20列的数是多少,观察对角线上元素,有一定的规律可循:(前4个对角线上的元素如下)1 5 13 25
可以看出
int res0 = 1;
res1 = 1 = res0 + (1 - 1) * 4;
res2 = 5 = res1 + (2 - 1) * 4;
res3 = 13 = res2 + (3 - 1) * 4;
res4 = 25 = res 3 + (4 - 1) * 4
找到如下的规律就可直接很快的解决问题了:
C++ 代码:
#include
using namespace std;
int main()
{
int res = 1;
for(int i = 2; i <= 20; i ++ )
{
res += 4*(i - 1);
cout << i << " : " << res << endl;
}
return 0;
}
//761
但是本着精益求精的态度,我们给出模拟蛇形填数的代码
C++ 代码:
#include
using namespace std;
const int N = 30;
int a[N][N];
int main()
{
int n = 21, num = 21 * 21;
for(int i = 1, cnt = 1; i <= n && cnt <= num; i++)
{
if(i & 1)
{
for(int x = i, y = 1; x >= 1 && y <= i; x--, y++)
a[x][y] = cnt++;
}
else
{
for(int x = 1, y = i; x <= i && y >= 1; x++, y--)
a[x][y] = cnt++;
}
}
printf("%d\n", a[20][20]);
return 0;
}
// 761
题目描述: 小蓝每天都锻炼身体。
正常情况下,小蓝每天跑 1 千米。如果某天是周一或者月初(1 日)为了激励自己,小蓝要跑 2 千米。如果同时是周一或月初,小蓝也是跑 2千米。小蓝跑步已经坚持了很长时间,从 2000 年 1 月 1 日周(含)到 2020 年10 月 1 日周四(含)。请问这段时间小蓝总共跑步多少千米?
题解: 我们打比赛最喜欢的大模拟题,谁知道那个抽风的出题人
C++代码:
#include
#include
#include
using namespace std;
int mouths[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool isleap(int yr)
{
return yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0;
}
int main()
{
int res = 0, sum = 0;
int week = 5;
for(int y = 2000;y < 2020; y ++ )
{
if(isleap(y)) mouths[2] = 29;
else mouths[2] = 28;
for(int i = 1; i <= 12; i ++ )
{
for(int j = 1;j <= mouths[i]; j ++ )
{
if((j == 1 && week == 0) || j == 1 || week == 0) res ++;
week ++;
week %= 7;
sum ++;
}
}
}
mouths[2] = 29;
for(int i = 1; i <= 9; i ++ )
{
for(int j =1 ; j <= mouths[i]; j ++)
{
if((j == 1 && week == 0 ) || j == 1 || week == 0) res ++;
week ++;
week %= 7;
sum ++ ;
}
}
cout << res + sum + 2 << endl;
return 0;
}
// 8879
看到一个大佬的写法,感觉很nice,代码简洁优雅
#include
#include
#include
#include
using namespace std;
int mouths[13] ={0,31,28,31,30,31,30,31,31,30,31,30,31};
int cnt = 5; //星期几
int ans = 0; //答案
bool check(int date) //检查当前日期是否合法
{
int year = date/10000;
int mouth = date%10000/100;
int day = date%100;
if(mouth<1||mouth>12)return false;
if(day<1)return false;
bool leap = (year%4==0&&year%100!=0)||(year%400==0); //判断是闰年还是平年
if(mouth!=2&&day>mouths[mouth])return false; //不是二月份是否合法
if(mouth==2&&day>mouths[mouth]+leap)return false; //是二月份是否合法
return true;
}
int main()
{
int start = 20000101;
for(int i=start;i<=20201001;i++)
{
if(check(i)) //检查i是否合法
{
cnt++; //判断星期几
ans++; //累计结果
int day = i%100; //计算当月天数
if(cnt%7==1||day==1)ans++; //如果是周一或者月初,结果再加1;
}
}
cout<<ans<<endl;
return 0;
}
我看到有人用excel 解决了这个问题,哈哈哈,本着精益求精的态度,然后小编没学会。
Excel解决,牛啊牛啊
题目描述:
问题描述:七段数码管的a,b,c,d,e,f,g的七个灯,问你最多可以表示为多少种字符(必须要求灯相连)
题解: dfs + 并查集,dfs枚举,并查集check
C++ 代码:
#include
#include
#include
#include
using namespace std;
const int N = 10;
bool st[N];
int ans;
int g[N][N], p[N];
/*
题目思路:dfs + 并查集
dfs 枚举所有情况
并查集 检查是否连通
*/
void init()
{
/*
连边建图, g[i][j] = 1, 表示i和j相邻
a b c d g f g
1 2 3 4 5 6 7
*/
g[1][2] = g[1][6] = 1;
g[2][1] = g[2][7] = g[2][3] = 1;
g[3][2] = g[3][4] = g[3][7] = 1;
g[4][3] = g[4][5] = 1;
g[5][4] = g[5][6] = g[5][7] = 1;
g[6][1] = g[6][5] = g[6][7] = 1;
}
int find(int x)
{
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
void dfs(int u)
{
if(u > 7)
{
//并查集初始化
for(int i = 1; i <= 7; i ++ ) p[i] = i;
//并查集合并
for(int i = 1; i <= 7; i ++ )
{
for(int j = 1; j <= 7; j ++ )
if(g[i][j] && st[i] && st[j])
{
int px = find(i), py = find(j);
if(px != py) p[px] = py;
}
}
//判断集合个数,如果只有一个集合则符合条件
int k = 0;
for(int i = 1; i <= 7; i ++ )
if(st[i] && p[i] == i) k ++;
if(k == 1) ans ++;
return;
}
//使用当前灯
st[u] = true, dfs(u + 1);
//不使用当前灯
st[u] = false, dfs(u + 1);
}
int main()
{
init();
dfs(1);
cout << ans << endl;
return 0;
}
// 80
内心os:终于用到数据结构了qaq
题目描述:
小蓝给学生们组织了一场考试,卷面总分为 100 分,每个学生的得分都是一个 0 到 100 的整数。如果得分至少是 60 分,则称为及格。果得分至少为 85 分,则称为优秀。请计算及格率和优秀率,用百分数示,百分号前的部分四舍五入保留整数。
输入描述
输入的第一行包含一个整数 n,表示考试人数。
接下来 n 行,每行包含一个 0 至 100 的整数,表示一个学生的得分。
输出描述
输出两行,每行一个百分数,分别表示及格率和优秀率。百分号前的部分四舍五入保留整数。
样例输入
7
80
92
56
74
88
100
0
样例输出
71%
43%
Hint
【评测用例规模与约定】 对于 50% 的评测用例,1 ≤ n ≤ 100。 对于所有评测用例,1 ≤ n ≤ 10000。
题解: 模拟吧,然后需要注意的就是四舍五入和怎样表示百分数
C++ 代码:
#include
#include
using namespace std;
const int N = 10010;
int n;
int main()
{
int a = 0, b = 0;
cin >> n;
int nn = n;
while(n -- )
{
int x; cin >> x;
if(x >= 60) a ++;
if(x >= 85) b ++;
}
int a1 = a * 100 / nn;
int b1 = b * 100 / nn;
double d1 = a1 * 1.0 / nn * 100 - a1;
double d2 = b1 * 1.0 / nn * 100 - b1;
if(d1 >= 0.5) a1 ++;
if(d2 >= 0.5) b1 ++;
printf("%d%%\n", a1);
printf("%d%%\n", b1);
return 0;
}
题目描述:
2020 年春节期间,有一个特殊的日期引起了大家的注意:2020 年 2 月 2日。因为如果将这个日期按 “yyyymmdd” 的格式写成一个 8 位数是 20200202,恰好是一个回文数。我们称这样的日期是回文日期。
有人表示 20200202 是 “千年一遇” 的特殊日子。对此小明很不认同,因为不到 2 年之后就是下一个回文日期:20211202 即 2021 年 12 月 2 日。
也有人表示 20200202 并不仅仅是一个回文日期,还是一个 ABABBABA型的回文日期。对此小明也不认同,因为大约 100 年后就能遇到下一个ABABBABA 型的回文日期:21211212 即 2121 年 12 月 12 日。算不上 “千年一遇”,顶多算 “千年两遇”。
给定一个 8 位数的日期,请你计算该日期之后下一个回文日期和下一个ABABBABA 型的回文日期各是哪一天。
输入描述
输入包含一个八位整数 N,表示日期。
输出描述
输出两行,每行 1 个八位数。第一行表示下一个回文日期,第二行表示下一个 ABABBABA 型的回文日期。
样例输入
20200202
样例输出
20211202
21211212
提示
【评测用例规模与约定】 对于所有评测用例,10000101 ≤ N ≤ 89991231,保证 N 是一个合法日期的8 位数表示。
题解: 太棒了,又是我们最喜爱的大模拟题,哇塞
C++ 代码:
#include
#include
#include
using namespace std;
const int s = 1000, e = 8999;
int n;
vector<int> vec;
// 判断回文日期是否合理
int mouths[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool check(int y, int m, int d)
{
if(m <= 12 && m >= 1)
{
if(y % 4 == 0 && y % 100 != 0 || y % 400 == 0) mouths[2] = 29;
if(mouths[m] >= d) return true;
else return false;
}
return false;
}
// 构造回文日期
void init()
{
for(int i = s; i <= e; i ++ )
{
string s = to_string(i);
for(int j = 3; j >= 0; j -- ) s += s[j];
int m = (s[4] - '0') * 10 + (s[5] - '0');
int d = (s[6] - '0') * 10 + (s[7] - '0');
if(check(i, m, d)) vec.push_back(stoi(s));
mouths[2] = 28;
}
}
// 二分找到第一个比输入大的回文日期
int ff(int idx)
{
int l, r, mid;
l = 0, r = vec.size();
while(l < r)
{
mid = l + r >> 1;
if(vec[mid] > idx) r = mid;
else l = mid + 1;
}
return l;
}
int ab(int idx)
{
for(int i = idx; i < vec.size(); i ++ )
{
string s = to_string(vec[i]);
if(s[0] == s[2] && s[1] == s[3]) return i;
}
return -1;
}
int main()
{
int n; cin >> n;
init();
int idx = ff(n);
cout << vec[idx] << endl;
idx = ab(idx);
cout << vec[idx] << endl;
return 0;
}
问题描述:
对于一个字符串 S,我们定义 S 的分值 f(S ) 为 S 中出现的不同的字符个 数。例如 f(”aba”) = 2,f(”abc”) = 3, f(”aaa”) = 1。 现在给定一个字符串 S [0…n − 1](长度为 n),请你计算对于所有 S 的非空 子串 S [i… j](0 ≤ i ≤ j < n),f(S [i… j]) 的和是多少。
输入格式:
输入一行包含一个由小写字母组成的字符串 S。
输出格式
输出一个整数表示答案。
样例输入
ababc
样例输出
28
样例说明
a 1
ab 2
aba 2
abab 2
ababc 3
b 1
ba 2
bab 2
babc 3
a 1
ab 2
abc 3
b 1
bc 2
c 1
评测用例规模与约定
对于 20% 的评测用例,1 ≤ n ≤ 10; 对于 40% 的评测用例,1 ≤ n ≤ 100; 对于 50% 的评测用例,1 ≤ n ≤ 1000; 对于 60% 的评测用例,1 ≤ n ≤ 10000; 对于所有评测用例,1 ≤ n ≤ 100000。
题解: 这个题就是可以看出每个字母只贡献了他出现第一次的哪一个1,所以对于给定字符串的每一个子串,我们只需要考虑在某一个子字符串中某一个字母可以贡献多少次,即:这个字母当前出现的位置i 减去他上一次出现的位置pre[i] 的差 乘上 字符串.size() - i
res += (ll)(i - pre[i]) * (str.size() - i);
至于为什么时乘法,其实可考虑一个例子:
abc 我们来看b
b之前 可以构成子串且包含b的为:b bc 2个
b之后 可以构成子串且包含b的为:ab b 2个
所以包含b的所有子串:
b ab bc abc为 4个
故为乘法 其实可以看成一个乘法分配律,然后将重复的字符合并为一个
(b, bc) * (ab, b) = ab + abc + b + bc 这个意思
C++代码
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 100010;
int pre[N];
LL res = 0;
int last[26];
int n;
int main()
{
string s;
cin >> s;
for(int i = 0; i < 26;i ++ ) last[i] = -1;
for(int i = 0; i < s.size(); i ++ )
{
int x = s[i] - 'a';
pre[i] = last[x];
last[x] = i;
}
for(int i = 0; i < s.size(); i ++ )
res += (LL)(i - pre[i]) * (s.size() - i);
cout << res << endl;
return 0;
}
这是比较巧的一种做法,其实可以dp做;本着精益求精的态度,哈哈哈
C++代码:
#include
#include
using namespace std;
map<char,long long> h;
const long long N=1e6+5;
long long dp[N];
long long p;
string str;
int main(){
cin>>str;
int len = str.size();
dp[0] = 1;
h[str[0]] = 1;
for(long long i = 1;i < len; i ++)
{
if(h[str[i]])
{
p += h[str[i]];
dp[i] = (i + 1) * (i + 2) / 2 - p + dp[i - 1];
h[str[i]] = i + 1;
}
else
{
dp[i] = (i + 2) * (i + 1) / 2 - p + dp[i - 1];
h[str[i]] = i + 1;
}
}
cout << dp[len - 1] << endl;
return 0;
}
题目描述
平面上有 N 条直线,其中第 i 条直线是 y = Ai· x + Bi。 请计算这些直线将平面分成了几个部分。
输入描述
第一行包含一个整数 N。 以下 N 行,每行包含两个整数 Ai, Bi。
输出描述
一个整数代表答案。
样例输入输出
Input
3
1 1
2 2
3 3
Output
6
评测用例规模与约定
对于 50% 的评测用例,1 ≤ N ≤ 4, − 10 ≤ A i , B i ≤ 10 −10 ≤ A_i, B_i ≤ 10 −10≤Ai,Bi≤10。 对于所有评测用例,1 ≤ N ≤ 1000, − 100000 ≤ A i , B i ≤ 100000 −100000 ≤ A_i, B_i ≤ 100000 −100000≤Ai,Bi≤100000。
题解:
在纸上画一画其实可以发现,因为我们添加的都是直线,所以:
C++代码:
#include
#include
using namespace std;
const int N = 1010;
typedef long long LL;
typedef long double LB;
typedef pair<LB, LB> PBB;
LB s[N][2];
LL ans;
bool st[N];
PBB p;
int main()
{
int n; cin >> n;
for(int i = 0; i < n; i ++ )
{
cin >> s[i][0] >> s[i][1];
set<PBB> pp;
for(int j = 0; j < i; j ++ )
{
if(st[j]) continue;
if(s[i][0] == s[j][0])
{
if(s[i][1] == s[j][1])
{
st[i] = true;
break;
}
else continue;
}
p.first = (s[j][1] - s[i][1]) / (s[i][0] - s[j][0]);
p.second = s[i][0] * p.first + s[i][1];
pp.insert(p);
}
if(!st[i]) ans += pp.size() + 1;
}
cout << ans + 1 << endl;
return 0;
}
题目描述:
小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。在冒泡排序中,每次只能交换相邻的两个元素。小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符,则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。例如,对于字符串 lan 排序,只需要 1 次交换。对于字符串 qiao 排序,总共需要 4 次交换。小蓝的幸运数字是 V,他想找到一个只包含小写英文字母的字符串,对这个串中的字符进行冒泡排序,正好需要 V 次交换。请帮助小蓝找一个这样的字符串。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,请告诉小蓝字典序最小的那个。请注意字符串中可以包含相同的字符。
题解: dfs + 减枝
C++代码:
#include
using namespace std;
const int N = (int)1e4+5;
int num[N], res[N];
int n, m, _max, len;
bool judge(int letter) {
int i = 26, j = letter;
while (!res[i]) i--;
if (i == j) {
while (i > 0 && j > 0) {
if (res[i] != num[j]) {
return res[i] > num[j];
} else {
i--; j--;
}
}
}
return i > j;
}
void dfs(int letter, int curlen, int cursum, int l) {
if (cursum > n) return ;
if (letter > _max) return ;
if (curlen > len) return ;
if (curlen == len && cursum != n) return ;
if (letter == _max && cursum != n) return ;
if (cursum == n) {
if (curlen < len || judge(letter)) { //长度减小或字典序减小更新结果
len = curlen;
for (int i = 1; i <= 26; i++) {
res[i] = num[i];
}
}
return ;
}
for (int i = 1; i <= l; i++) {
num[letter + 1] = i;
dfs(letter + 1, curlen + i, cursum + i * curlen, i);
}
num[letter + 1] = 0;
}
int main() {
scanf("%d", &n);
m = 0; len = 0;
while (m < n) {
int id = 1;
for (int i = 2; i <= 26; i++) { //找到s最小的点, 如果存在多个取字典序更小的
if (res[i] < res[id]) id = i;
}
m += len - res[id];
_max = max(_max, id);
len ++; res[id] ++;
}
dfs(0, 0, 0, 10);
for (int i = _max; i >= 1; i--) {
for (int j = res[i]; j > 0; j--) {
printf("%c", i-1+'a');
}
}
printf("\n");
return 0;
}
总结:自己太菜了