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;
}

15分代码如下:采用输出统计数字六的数量。
点击查看代码
#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>的题,并查集初始化,查找,合并,路径压缩,我从这道题上学会的,还可以用vector<seint>也可以起到判断两个数的关系。
题目如下:

代码如下:

点击查看代码
#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;
            }


  }
}

7-10 红色警报 题解: 核心逻辑:若某城市被攻陷,则将其在地图上删除,若删除后的连通块数量增加,则该城市为核心城市。

技巧:设置一个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;
}


解法2: 结构体+并查集(这个时间复杂度更低)
点击查看代码
#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>arr;**就可以从大到小记录每一个数。
然后我们就可以知道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);
        }
    }
}

7-12 文件传输 题解:这道题目依旧是并查集,不过我当时没有想到用并查集,用set维护,可以骗11分,正确的做法就是并查集。 一道简单的并查集,最后就是一个小问题,如何判断是否都在一个集合里面我们只需要

一个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);
           }

       }
    }
7-9 哈利·波特的考试 7-10 列车厢调度 题解:用栈存 7-11 病毒溯源 补题中----- * **atcoder** 我只写出了两道题,我分享一下第二道题,我感觉是字符串一个很好用的寻找子串的方法。我们只需要建立两个for循环,然后让strsub方法去截取里面的字符,用set维护即可找到不同的子串 题目如下:

代码如下:

点击查看代码
#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;
}
posted @ 2024-03-31 16:29  冬天的睡袋  阅读(17)  评论(0编辑  收藏  举报