文章目录
L1全是语法题,用不着算法,L2全是算法题,L3是进阶题。
以下整理的就是我不会的,或者我觉得方法比我好的题目。
L1-3 强迫症(字符串的处理)
觉得比较好的做法:我觉得就是比较简便,然后就是也是更偏向喜欢用函数
substr()是字符提取函数。
stoi()是将字符串转化为数字。
#include<bits/stdc++.h>
using namespace std;
string n;
int main(){
cin>>n;
if(n.size()==4){
if(stoi(n.substr(0,2))<22) cout<<"20"<<n.substr(0,2)<<"-"<<n.substr(2,4)<<endl;
else cout<<"19"<<n.substr(0,2)<<"-"<<n.substr(2,4)<<endl;
}
else cout<<n.substr(0,4)<<"-"<<n.substr(4,5);
}
我的做法:
#include<bits/stdc++.h>
using namespace std;
int main()
{
string s;
cin>>s;
if(s.size()==6)
for(int i=0;i<6;i++)
{
if(i==4)cout<<"-";
cout<<s[i];
}
else
{
int k=(s[0]-'0')*10+(s[1]-'0');
if(k<22)cout<<"20";
else cout<<"19";
cout<<s[0]<<s[1]<<"-";
for(int i=2;i<4;i++)
cout<<s[i];
}
return 0;
}
L1-6 吉老师的回归(字符串的处理)
输入样例1:
5 1
L1-1 is a qiandao problem.
L1-2 is so...easy.
L1-3 is Easy.
L1-4 is qianDao.
Wow, such L1-5, so easy.
输出样例1:
L1-4 is qianDao.
输入样例2:
5 4
L1-1 is a-qiandao problem.
L1-2 is so easy.
L1-3 is Easy.
L1-4 is qianDao.
Wow, such L1-5, so!!easy.
输出样例2:
Wo AK le
我感觉这道题目,就是我的代码跟我觉得好的代码差不多,但是我的思路不清晰,有点乱。
比较清晰的做法:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, m;
string str;
signed main() {
cin >> n >> m;
getchar();//跳过回车
for (int i = 1; i <= n; i ++) {
getline(cin, str);
if (str.find("qiandao") != -1 || str.find("easy") != -1) continue;
if (!m) {
cout << str << endl;
return 0;
}
m --;
}
cout << "Wo AK le" << endl;
}
我的写法:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m;
cin>>n>>m;
string s;
string s1="easy";
string s2="qiandao";
getchar();//清除回车
for(int i=0;i<n;i++)
{
getline(cin,s);
// cout<<s<<endl;
if(m==0)
{
if(s.find(s1)!=-1||s.find(s2)!=-1)
cout<<"Wo AK le";
else cout<<s;return 0;
}
if(s.find(s1)==-1&&s.find(s2)==-1)//说明这道题目不是签到题目
m--;
}
cout<<"Wo AK le";
return 0;
}
L1-8 乘法口诀数列(字符串的处理)
我这一个就是最后一个样例点卡着没过,我疑惑了蛮久的,重新写一遍过了,但是之前的那种方法不知道为啥没过,后来才知道,我在分解数的时候,如果分解的是个整十的数,那么结尾的0是可能保存不下来的。
然后我的写法也没有别人好,还是字符串的应用吧,牛逼!
比较好的写法:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, i;
string a, b, res;
signed main() {
cin >> a >> b >> n;
res = a + b;
while (res.size() < n) {
int a = res[i] - '0', b = res[i + 1] - '0';
res += to_string(a * b);
i ++;
}
for (int i = 0; i < n; i ++) cout << res[i] << " \n"[i == n - 1];//这个后面的限制条件只对换行有用!
}
我的写法:
#include<bits/stdc++.h>
using namespace std;
int num[1005];
int main()
{
int a1,a2,n;
cin>>a1>>a2>>n;
num[1]=a1;
num[2]=a2;
int m=3;
for(int i=1;i<=n;i++)
{
int x=num[i]*num[i+1];
if(x>=10)
{
num[m++]=x/10;
x=x%10;
}
num[m++]=x;
if(m>n)break;
}
for(int i=1;i<=n;i++)
{
if(i!=1)printf(" %d",num[i]);
else printf("%d",num[i]);
}
return 0;
}
L2-2 病毒溯源(树形dp,记录路径)
输入样例:
10
3 6 4 8
0
0
0
2 5 9
0
1 7
1 2
0
2 3 1
输出样例:
4
0 4 9 1
这道题抽象题意,找根节点到叶节点的最长路径。
如果有多个答案,输出字典序最小的。我们还要记录一下路径。
我们记录路径只需要记录怎么转移的。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010, M = 10010;
int n;
int h[N], e[M], ne[M], idx;//邻接表
int son[N];
bool st[N];//看下那个点是没有父节点的
void add(int a, int b) // 添加一条边a->b
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
//关键的事情说三遍:h数组的下标为结点的编号,e,ne数组的下标为边的编号,idx为边的编号
}
int dfs(int u)
{
int res = 0;
son[u] = -1;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
int d = dfs(j);//从j走下去,会有多深
if (res < d) res = d, son[u] = j;
else if (res == d) son[u] = min(son[u], j);
}
return res + 1;//加的是当前这个点
}
int main()
{
memset(h, -1, sizeof h);
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
int cnt;
scanf("%d", &cnt);
while (cnt -- )
{
int x;
scanf("%d", &x);
add(i, x);
st[x] = true;
}
}
int root = 0;
//找根节点
while (st[root]) root ++ ;
printf("%d\n", dfs(root));
printf("%d", root);
while (son[root] != -1)
{
root = son[root];
printf(" %d", root);
}
return 0;
}
L 2-3 清理代码库(STL的应用!!好好领会)
输入样例:
7 3
35 28 74
-1 -1 22
28 74 35
-1 -1 22
11 66 0
35 28 74
35 28 74
输出样例:
4
3 35 28 74
2 -1 -1 22
1 11 66 0
1 28 74 35
记录一下每串代码。
我们统计一个东西出现了多少次可以用哈希表。
由于数据范围很小,我们开一个map来做。开一个map<vector,int>,vector可以用比较函数,我们再将每一个二元组存到pair里面,排序输出。
pair默认从小往大排序。
考察STL,首先,统计的话很容易就想到了用map来做,用vector来存(注意这里,map的自动排序会按照vector的字典序,也就是每一行的字典序从小到大来排序),所以根据括号,我们还需要一个vector来重新储存去重之后的这些值,所以就定义一个vector<
pair… > > ans,之后再用一发sort,就会按照出现次数来排序了
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#define x first
#define y second
using namespace std;
const int N = 10010;
int n, m;
map<vector<int>, int> cnt;
vector<pair<int, vector<int>>> ans;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ )
{
vector<int> line;
for (int j = 0; j < m; j ++ )
{
int x;
scanf("%d", &x);
line.push_back(x);
}
cnt[line] ++ ;
}
for (auto& p: cnt) ans.push_back({-p.y, p.x});//因为pair默认是从小到大排序,所以我们这里就加一个负号,让他从大到小排序
sort(ans.begin(), ans.end());
printf("%d\n", cnt.size());
for (auto& p: ans)
{
printf("%d", -p.x);
for (auto x: p.y)
printf(" %d", x);
puts("");
}
return 0;
}
L2-4 哲哲打游戏(模拟)
输入样例:
10 11
3 2 3 4
1 6
3 4 7 5
1 3
1 9
2 3 5
3 1 8 5
1 9
2 8 10
0
1 1
0 3
0 1
1 2
0 2
0 2
2 2
0 3
0 1
1 1
0 2
输出样例:
1
3
9
10
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 100010, M = 110;
int n, m;
vector<int> g[N];
int record[M];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
{
int cnt;
scanf("%d", &cnt);
while (cnt -- )
{
int x;
scanf("%d", &x);
g[i].push_back(x);//有点邻接表的感觉
}
}
int p = 1;
while (m -- )
{
int a, b;
scanf("%d%d", &a, &b);
if (a == 0)//进入下一个剧情
{
p = g[p][b - 1];
}
else if (a == 1)//存档
{
record[b] = p;
printf("%d\n", p);
}
else//读取存档
{
p = record[b];
}
}
printf("%d\n", p);
return 0;
}
L3-1 森森旅行(单源最短路)
输入样例:
6 11 3
1 2 3 5
1 3 8 4
2 4 4 6
3 1 8 6
1 3 10 8
2 3 2 8
3 4 5 3
3 5 10 7
3 3 2 3
4 6 10 12
5 6 10 6
3 4 5 2 5 100
1 2
2 1
1 17
输出样例:
8
8
1
这道题目,我们可以分为两段,假设他在城市p这个地方,把所有的现金换成了旅游金,那我就可以知道了他所有的花费就是1~p的现金 + p ~ n的旅游金所组成,所以我们最后只需要把前面的最小值和后面的最小值求出来就可以了。所以最后我们的问题转化为了单源最短路。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
using namespace std;
typedef long long LL;
typedef pair<LL, int> PII;
const int N = 100010, M = 200010 * 2;
const LL INF = 0x3f3f3f3f3f3f3f3fll;
int n, m, Q;
//dijstra的邻接表
int h1[N], h2[N], e[M], w[M], ne[M], idx;//正向反向
LL dist1[N], dist2[N];//1~p,p~n
bool st[N];
int ratio[N];
void add(int h[], int a, int b, int c) // 添加一条边a->b,边权为c
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
void dijkstra(int h[], LL dist[], int start) // 求1号点到n号点的最短路距离
{
memset(dist, 0x3f, sizeof dist1);//在函数当中,dist是个指针,我们要用全局变量的真正数组才可以
memset(st, 0, sizeof st);//这是由于dijstra要调用两次
dist[start] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, start});
while (heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[ver] + w[i])
{
dist[j] = dist[ver] + w[i];
heap.push({dist[j], j});
}
}
}
}
int main()
{
scanf("%d%d%d", &n, &m, &Q);
memset(h1, -1, sizeof h1);
memset(h2, -1, sizeof h2);
while (m -- )
{
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
add(h1, a, b, c), add(h2, b, a, d);
}
for (int i = 1; i <= n; i ++ ) scanf("%d", &ratio[i]);
dijkstra(h1, dist1, 1);
dijkstra(h2, dist2, n);
multiset<LL> S;
for (int i = 1; i <= n; i ++ )//从前往后枚举我们的中间点
if (dist1[i] != INF && dist2[i] != INF)//判断是否连通
{//可以到i,且i可以到终点
S.insert(dist1[i] + (dist2[i] + ratio[i] - 1) / ratio[i]);
}
while (Q -- )
{
int a, b;
scanf("%d%d", &a, &b);
if (dist1[a] != INF && dist2[a] != INF)
{
S.erase(S.find(dist1[a] + (dist2[a] + ratio[a] - 1) / ratio[a]));
ratio[a] = b;
S.insert(dist1[a] + (dist2[a] + ratio[a] - 1) / ratio[a]);
}
printf("%lld\n", *S.begin());
}
return 0;
}
L3-2 还原文件(字符串哈希)
输入样例:
17
95 70 80 97 97 68 58 58 80 72 88 81 81 68 68 60 80
6
4 68 58 58 80
3 81 68 68
3 95 70 80
3 68 60 80
5 80 72 88 81 81
4 80 97 97 68
输出样例:
3 6 1 5 2 4
判断两个字符串是否相等,可以用哈希。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ULL;
const int N = 100010, M = 110, P = 131;//131是字符串哈希的经验值
int n, m;
ULL h[N], p[N];
int width[M];
ULL g[M];
bool st[M];
int ans[M];
ULL get(int l, int r) // 计算子串 str[l ~ r] 的哈希值
{
return h[r] - h[l - 1] * p[r - l + 1];
}
bool dfs(int u, int end)
{
if (end == n) return true;
for (int i = 1; i <= m; i ++ )
if (!st[i] && g[i] == get(end, end + width[i] - 1))
{
st[i] = true;
ans[u] = i;
if (dfs(u + 1, end + width[i] - 1)) return true;
st[i] = false;
}
return false;
}
int main()
{
scanf("%d", &n);
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
int x;
scanf("%d", &x);
p[i] = p[i - 1] * P;
h[i] = h[i - 1] * P + x + 1;//字符串哈希不能出现0
}
scanf("%d", &m);
for (int i = 1; i <= m; i ++ )
{
scanf("%d", &width[i]);
for (int j = 0; j < width[i]; j ++ )
{
int x;
scanf("%d", &x);
g[i] = g[i] * P + x + 1;
}
}
dfs(1, 1);
for (int i = 1; i <= m; i ++ )
{
printf("%d", ans[i]);
if (i != m) printf(" ");
}
return 0;
}
L3-3 可怜的简单题
如果可以拿一分,何乐而不为呢?嘻嘻
#include<bits/stdc++.h>
using namespace std;
int main(){
ll a,b;
cin>>a>>b;
cout<<"1";
return 0;
}
同步流
关闭同步流之后就不能用getchar()了,要换成cin.get()。我是说怎么很奇怪,清空回车没有用了。关于关闭同步流,还有很多其他的注意点,可以看这篇文章。(ios::sync_with_stdio(false))