SUM-ACM,3月24-3-31周报
两场天梯赛和一场atcoder。
主要错误知识点在于字符串的处理和并查集的掌握不够,不懂灵活运用。
- 第一场pta天梯赛
7-5 6翻了
一道字符串的题,我只拿了14分。我不熟悉一个点,f(i,0,s.length())是有问题的,在replace后,s.length()会改变,这个时候replace不是一个好的选择。
我们只需要输出的时候统计,而不需要处理再输出,边输出边处理即可。
题目如下:
14分代码如下——采用relapce的形式:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int main ()
{
int b=0;
int res =0;
string s;
getline(cin,s);
for(int i=0;i<=s.length();i++){
if(s[i]!='6')
{
if(res>3&&res<=9) {
s.replace(b, res, "9");
}
else if(res>9)
{
s.replace(b,res,"27");
}
res= 0;
b=0;
}else
{
res++;
if(res==1)
b=i;
}
}
cout<<s;
}
点击查看代码
#include <bits/stdc++.h>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define f(i,a,n) for(int i=a;i<n;++i)
#define ff(i,a,n) for(int i=a;i<=n;++i)
const int INF=0x3f3f3f3f;
using namespace std;
typedef long long ll;
//
string s;
int main(){
getline(cin,s);
s=s+'q';
int cnt=0;
for(int i=0;i<s.length();i++){
if(s[i]!='6'){
if(cnt>9){
cout<<"27";
}
else if(cnt>3){
cout<<"9";
}
else {
f(j,0,cnt)cout<<6;
}
if(i!=s.length()-1)cout<<s[i];
cnt=0;
}
else{
cnt++;
}
}
return 0;
}
7-7 估值一亿的AI核心代码
这道题也是一道字符串的题,这道题的题面不清晰,而且坑有点多。学长教的做法,先把简单容易判断的先实现比如先输出前后的空格,剩下两个一个是I could 和I,me等,我们需要特殊处理,比如说I could可以变成Is could ,后面判断的时候有一个大写字母即可区分。
题目如下:
代码如下:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll get(int x) {
ll res = 1;
for (int i = 2; i <= x; i++) {
res *= i;
}
return res;
}
int main () {
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin >> n;
string s;
getline(cin, s);
while (n--) {
getline(cin, s);
cout << s << "\nAI: ";
while (s.back() == ' ') s.pop_back();
reverse(s.begin(), s.end());
while (s.back() == ' ') s.pop_back();
reverse(s.begin(), s.end());
string ans;
int cnt = 0;
for (auto& c : s) {
if (c == ' ') {
cnt++;
} else if (isalpha(c) || isdigit(c)) {
if (cnt) ans += ' ';
if (isupper(c) && c != 'I') c -= 'A' - 'a';
ans += c;
cnt = 0;
} else {
if (c == '?') c = '!';
ans += c;
cnt = 0;
}
}
auto f = [&] (const string& k, const string&v) {
int now = -1;
while ((now = ans.find(k, now + 1)) != ans.npos) {
int ok = 0;
if (now - 1 < 0 || !isalpha(ans[now-1]) && !isdigit(ans[now-1])) {
ok++;
}
if (now + k.size() >= ans.size() || !isalpha(ans[now + k.size()]) && !isdigit(ans[now + k.size()])) {
ok++;
}
if (ok == 2) {
ans.erase(now, k.size());
ans.insert(now, v);
}
}
};
f("can you", "IS can");
f("could you", "IS could");
f("I", "you");
f("me", "you");
for (int i = 0; i < ans.size(); i++) {
cout << ans[i];
if (ans[i] == 'I' && i + 1 < ans.size() && ans[i+1] == 'S') {
i++;
}
}
cout << '\n';
}
}
7-8 前世档案
这道题虽然我AC了,但我觉得还是需要放在题解里,当时我看到这道题目知道是二叉树,虽然我知道二叉树,但是它的代码我还不会写,后面找出了其中的规律,一个一个节点可以用二进制的形式表示出来,于是问题转化成了已知二进制数怎么转为整数的问题。然后我用一个pow(2,i)解决问题。
题目如下:
代码如下:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int main ()
{
int n,m;
cin>>n>>m;
getchar();
while(m--)
{
int ans =1;
string s;
getline(cin,s);
for(int i=0;i<s.length();i++){
if(s[i]=='y')
s[i]='0';
else
s[i]='1';
}
// cout<<s<<"\n";
// reverse(s.begin(),s.end());
for(int i=0;i<s.length();++i)
{
if(s[i]=='1')
ans+=pow(2,s.length()-1-i);
}
cout<<ans<<endl;
}
}
7-11 排座位
题解:这道题是并查集加上一个vector<set
题目如下:
代码如下:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 100010
int f[N];
int find(int k)//并查集中的路径压缩
{
if(f[k]==k)return k;
return f[k]=find(f[k]);
}
void merge(int v,int u) {//合并子集
int t1,t2;//t1,t2分别为v和u的祖宗
t1=find(v);//获取祖宗结点值
t2=find(u);
if (t1!=t2) f[t2]=t1;//靠左。即将右边的集合,作为左边的子集
return;
}
int main ()
{
int k;
int n,m;
vector <set<int> > vs(n);
cin>>n>>m>>k;
for (int i = 1; i <= n; ++i) {
f[i] = i;
}
while(m--)
{
int x,y,z;
cin>>x>>y>>z;
if(z==1)
merge(x,y);
else {
vs[x].insert(y);
vs[y].insert(x);
}
}
while (k--)
{
int x,y;
cin>>x>>y;
int res =0;
if(find(x)==find(y))
{
res++;
}
if(vs[x].count(y))
{
res++;
}
if(res==0)
{
cout<<"OK"<<"\n";
}else if(res==2)
{
cout<<"OK but..."<<"\n";
}else
{
if(find(x)==find(y))
cout<<"No problem"<<endl;
else
cout<<"No way"<<endl;
}
}
}
技巧:设置一个del数组,代表该城市是否被攻陷,若是,则不参与计数。
解法一:DFS
建立地图,建立关联,每攻陷一个城市,就深搜一次,判断连通块数量是否增加。
解法二:并查集
建立结构体数组,存储所有边的信息,每攻陷一个城市,就重新建立一次并查集,判断连通块数量是否增加(注意是重新建立,而不是搜索或查询)。
题目如下:
解法一: DFS
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int degree[510]; //每个节点的度数
int G[510][510]; //地图
int vis[510]; //每个节点是否出现过
int del[510]; //该节点是否被删除,置1表示被删除
int n, k;
void dfs(int step) {
for(int i = 0; i < n; i++)
if(vis[i] == 0 && del[i]==0 && G[step][i]) {
vis[i] = 1;
dfs(i);
}
}
int main() {
scanf("%d %d", &n, &k);
for(int i = 0; i < k; i++) {
int x, y;
scanf("%d %d", &x, &y);
G[x][y] = G[y][x] = 1;
}
//计算最初的连通块数量
int num_line = 0; //连通块数量
for(int i = 0; i < n; i++)
if(vis[i] == 0) {
vis[i] = 1;
num_line++;
dfs(i);
}
int m; scanf("%d", &m);
for(int i1 = 0; i1 < m; i1++) {
int x; scanf("%d", &x);
//改进:直接判断连通性,若大了,则发出警告
//删除与该点有关的信息
for(int i = 0; i < n; i++)
if(G[x][i] == 1) G[x][i] = G[i][x] = 0;
del[x] = 1;
//初始化vis
memset(vis, 0, sizeof(vis));
//判断连通块数量
int num_line_after = 0;
for(int i = 0; i < n; i++)
//如果没有遍历过,并且没被删除过
if(vis[i] == 0 && del[i]==0) {
vis[i] = 1;
num_line_after++;
dfs(i);
}
//判断删除点前后的连通块数量是否相同
if(num_line < num_line_after) printf("Red Alert: City %d is lost!\n", x);
else printf("City %d is lost.\n", x);
//更新连通块的数量
num_line = num_line_after;
if(i1 == n-1) printf("Game Over.\n");
}
return 0;
}
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct node{
int x, y;
}edge[5005];
int pre[510]; //存放并查集
int del[510]; //该节点是否被删除,置1表示被删除
int n, k;
int find(int x) {
return x == pre[x] ? x : pre[x] = find(pre[x]);
}
void Union(int x, int y) {
int fx = find(x);
int fy = find(y);
if(fx != fy) pre[fx] = fy;
// if(fx > fy) pre[fx] = fy;
// else pre[fy] = fx;
}
int main() {
scanf("%d %d", &n, &k);
for(int i = 0; i < n; i++) pre[i] = i; //并查集初始化
for(int i = 0; i < k; i++) {
scanf("%d %d", &edge[i].x, &edge[i].y);
Union(edge[i].x, edge[i].y);
}
int num_line_before = 0; //攻占某城市前连通块数量
for(int i = 0; i < n; i++) if(pre[i] == i) num_line_before++;
int m; scanf("%d", &m);
for(int i1 = 0; i1 < m; i1++) {
int x; scanf("%d", &x);
del[x] = 1;
for(int i = 0; i < n; i++) pre[i] = i;
for(int i = 0; i < k; i++) {
if(!del[edge[i].x] && !del[edge[i].y]) {
Union(edge[i].x, edge[i].y);
}
}
int num_line_after = 0; //攻占某城市后连通块数量
for(int i = 0; i < n; i++) if(pre[i] == i && !del[i]) num_line_after++;
if(num_line_after > num_line_before) printf("Red Alert: City %d is lost!\n", x);
else printf("City %d is lost.\n", x);
if(i1 == n-1) printf("Game Over.\n");
num_line_before = num_line_after;
}
return 0;
}
- 第二场天梯赛
7-4 谁能进图书馆
题解:我当时拿了9分,满分10分,我分析了原因,我读题不清晰,不知道两个人都进不去有两行输出。
题目如下:
代码如下:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,m,k,l;
cin>>n>>m>>k>>l;
//禁入年龄线 陪同年龄线 询问者1的年龄 询问者2的年龄
if(k>=m&&l<n)
{
cout<<k<<"-Y"<<" ";
cout<<l<<"-Y";
cout<<"\n";
cout<<"qing 1 zhao gu hao 2";
}else if(k<n&&l>=m)
{
cout<<k<<"-Y"<<" ";
cout<<l<<"-Y";
cout<<"\n";
cout<<"qing 2 zhao gu hao 1";
}
else if(k<n&&l<n)
{
cout<<k<<"-N"<<" ";
cout<<l<<"-N";
cout<<"\n";
cout<<"zhang da zai lai ba";
}
else if(k<m&&k>n&&l<n)
{
cout<<k<<"-Y"<<" ";
cout<<l<<"-N";
cout<<"\n";
cout<<"1: huan ying ru guan";
}else if(k<n&&l>n&&l<m)
{
cout<<k<<"-N"<<" ";
cout<<l<<"-Y";
cout<<"\n";
cout<<"2: huan ying ru guan";
}
else
{
cout<<k<<"-Y"<<" ";
cout<<l<<"-Y";
cout<<"\n";
cout<<"huan ying ru guan";
}
}
7-7 出租
题解:由题目可知,第一个中数组的数都是不一样的,而且是从大到小,所以我们可以先解决上面的数组然后再搞定下面的数组,根据上面数组的特点,
我想到了堆,用** set<int,greater
然后我们就可以知道tle中每一个在index中的每一个数对应上面set的下标对应的值就可以得出下面数组的每一个值。有一个问题就是set是不会记录下标的,
因为堆的原理是二叉树,下标没有意义,但是这个题中是有意义的,我们可以用一个变量来记录第几位即可。
题目如下:
代码如下:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int main ()
{
set<int,greater<int>>arr;
vector<int>index;
string s;
cin>>s;
for(int i=0;i<s.length();i++){
arr.insert(s[i]-'0');
}
for(int i=0;i<s.length();i++){
int rs =0;
for ( set<int>::iterator it = arr.begin(); it != arr.end(); it++){
rs++;
if (*it == s[i]-'0')
{
index.push_back(rs);
}
}
}
cout<<"int[] arr = new int[]{";
int res =0;
for(set<int>::iterator i=arr.begin();i!=arr.end();i++)
{
res++;
cout<<*i;
if(res<arr.size())
cout<<",";
}
cout<<"};"<<endl;
cout<<"int[] index = new int[]{";
for(int i=0;i<index.size();i++){
cout<<index[i]-1;
if(i<index.size()-1)
cout<<",";
}
cout<<"};"<<endl;
}
7-7 连续因子
题解:1.一个数再sqrt(a)-a之间肯定不会有连续因子,所以从2-sqrt(a)循环
2.另用一个变量被循环的i赋值,寻找最长的连续因子。每次循环完将count回到0,并记录最长开始的i。
3.输出,先输出最开始,然后循环依次输出*和开始+1.
这种做法虽然拿不了满分,但是大部分样例都没问题,是拿分的一个好思路。
题目如下:
18分代码如下:
点击查看代码
#include <bits/stdc++.h>
int main()
{
int a,n,max=0;
int count,begin;
int b[10001];
scanf("%d",&a);
for(int i=2;i<=sqrt(a);i++)
{
n=a;
count=0;
int j=i;
while(n%j==0)
{
n/=j;
j++;
count++;
}
if(count>max)
{
max=count;
begin=i;
}
}
if(max)
{
printf("%d\n",max);
printf("%d",begin);
for(int c=1;c<max;c++)
{
printf("*");
printf("%d",begin+c);
}
}
}
一个for循环判断有多少个f[i]==i,然后cnt加加,因为都在一个集合里面只会有一个f[i]=i
如果cnt==1说明都在一起,否则输出cnt个
题目如下:
代码如下:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 100010
int f[N];
int del[N]; //该节点是否被删除,置1表示被删除
struct node{
int x, y;
}edge[5005];
int find(int k)//并查集中的路径压缩
{
if(f[k]==k)return k;
return f[k]=find(f[k]);
}
void merge(int v,int u) {//合并子集
int t1,t2;//t1,t2分别为v和u的祖宗
t1=find(v);//获取祖宗结点值
t2=find(u);
if (t1!=t2) f[t2]=t1;//靠左。即将右边的集合,作为左边的子集
return;
}
int main ()
{
int pd=0;
int res=0;
int n;
cin>>n;
for (int i = 1; i <=n ; ++i) {
f[i]=i;
}
char a;int x,y;
while(cin>>a)
{
if(a=='S')
{
for (int i = 1; i <=n ; ++i) {
if(f[i]==i)
{
res++;
}
}
if(res==1)
{
printf("The network is connected.");
}else
printf("There are %d components.",res);
}else
{
cin>>x>>y;
if(a=='C')
{
if(find(x)== find(y))
cout<<"yes"<<"\n";
else
cout<<"no"<<"\n";
}else if(a=='I')
merge(x,y);
}
}
}
代码如下:
点击查看代码
#include <iostream>
#include <string>
#include <set>
int countSubstrings(const std::string& S) {
std::set<std::string> substrings;
for (size_t i = 0; i < S.length(); ++i) {
for (size_t j = i+1; j <= S.length(); ++j) {
substrings.insert(S.substr(i, j-i));
}
}
return substrings.size();
}
int main() {
std::string S;
std::cin >> S;
int result = countSubstrings(S);
std::cout << result << std::endl;
return 0;
}