Loading

AtCoder Beginner Contest 302




B - Find snuke

题目大意

给定一个字符矩阵以及它的长和宽, 问在这个矩阵中是否存在水平, 竖直或者对角线方向上存在一串连续的字符串"sunke" (注意是只能沿着这三个方向的其中一个,中途不能转向), 如果存在则输出五个字符的坐标

解题思路

我们可以在输入矩阵的时候把所有's'的位置都存到vector里面, 之后我们只需要遍历vector里的坐标即可; 由题意得字符串的方向一共有8种可能, 可以先遍历's'坐标的8个方向, 如果有'u', 那么我们就可以沿着那个方向遍历一遍即可

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
char s[N][N];
int dx[] = { 1,0,-1,0,1,1,-1,-1 }, dy[] = { 0,1,0,-1,-1,1,-1,1 };
char str[5] = { 's','n','u','k','e' };
vector<PII> v;
vector<PII> z;
int n, m;
signed main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> s[i][j];
            if (s[i][j] == 's')   v.push_back({ i,j });
        }
    }
    for (auto t : v) {
        for (int i = 0; i < 8; i++) {
            z.push_back({ t.first,t.second });
            bool f = true;
            for (int j = 1; j <=4; j++) {
                int x = t.first + dx[i]*j;
                int y = t.second + dy[i]*j;
                if (x >= 1 && x <= n && y >= 1 && y <= m && s[x][y] == str[j]) {
                    z.push_back({ x,y });
                }
                else {
                    f = false;
                    z.clear();
                    break;
                }
            }
            if (f) {
                for (auto p : z) {
                    cout << p.first << ' ' << p.second << endl;
                }
                return 0;
            }
        }
    }
    return 0;
}




C - Almost Equal

题目大意

给定n个只由小写字母组成的相同长度的字符串, 你可以改变各个字符串之间的顺序, 问是否存在一种排序使得, 每一个字符串都与它下一个的字符串只有一个字符不同;
注意: 两个字符串的某个字符相同意思是该字符在两个字符串的位置也相同; eg: abed和abcd是合法的,因为他们只有e和c不同, 而abcd和dcba是不合法的, 他们四个字符都不同;

解题思路

这个题吧...我一直想不到什么好的方法; 最后看数量都很小, 死马当活马医用next_permutation排序来暴力做了一下, 没想到直接过了

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
typedef pair<int, int> PII;
string s[10];
int n, m;
signed main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> s[i];
	}
	sort(s + 1, s + 1 + n);
	do {
		bool f = true;
		for (int i = 1; i < n; i++) {
			int num = 0;
			for (int j = 0; j < m; j++) {
				if (s[i][j] != s[i + 1][j]) {
					num++;
					if (num == 2) {
						f = false;
						break;
					}
				}
			}
			if (!f) break;
		}
		if (f) {
			cout << "Yes";
			return 0;
		}
	} while (next_permutation(s + 1, s + 1 + n));
	cout << "No";
	return 0;
}




D - Impartial Gift

题目大意

小莫打算送给A和B一人一份礼物, 对于A和B, 小莫各有一份礼物价值名单a和b, 各自的礼物会从各自的礼物名单中选出; 现在有个要求是两人的礼物价值相差不能超过k; 问送给两人的礼物总价值最大为多少

解题思路

这个题的思路很简单, 无非就是先遍历名单a, 对于每个礼物的价值a[i], 我们要在b里面找出a[i]-k到a[i]+k这个区间里最大的数, 然后更新最大值即可; 这题难点主要在数据范围较大, 容易tle, 所以我们在查找时都用二分即可;

神秘代码

#include<bits/stdc++.h>
#define int  long long
using namespace std;
const int N = 1e6 + 10;
typedef pair<int, int> PII;
vector<int> a;
vector<int> b;
int n, m,k;
signed main() {
	cin >> n >> m >> k;
	for (int i = 1; i <= n; i++) {
		int x;
		cin >> x;
		a.push_back(x);
	}
	for (int i = 1; i <= m; i++) {
		int x;
		cin >> x;
		b.push_back(x);
	}
	sort(a.begin(), a.end());
	sort(b.begin(), b.end());
	int maxn = -1;
	for (int i = 0; i < n; i++) {
		int l = a[i] - k;
		int r = a[i] + k;
		auto c = lower_bound(b.begin(), b.end(), l);
		int res = c - b.begin();
		if (c == b.end()||b[res]>r) continue;
		auto d= upper_bound(b.begin(), b.end(), r);
		int idx = d - b.begin()-1;
		if (b[idx] < l) continue;
		maxn = max(maxn, a[i] + b[idx]);
	}
	cout << maxn;
	return 0;
}




E - Isolation

题目大意

题目给定了由1~n编号的n个顶点, 初始情况下没有边与他们相连; 接下来有k次操作, 每次操作有两种选择: 一是"1 u v" 表示建立一条边将顶点u和v相连; 二是"2 u" 表示删除所有与顶点u相连的边;
对于每一次操作结束后, 需要输出当前有多少个顶点是没有边与它们相连的

解题思路

题目问孤立点的个数, 但是我们更容易求出联通点的个数, 最后用总数减一下即可;
我们可以用一个set st[N]来储存每个点都有哪些点与其相连; 对于1操作直接插入即可, 对2操作我们可以用erase清楚其联通点与当前点的边, 再用clear处理当前点即可; 注意数量的变化, 什么时候+1或者-1;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N=1e6+10;
int n,k,idx;
set<int> st[N];
signed main(){
    cin>>n>>k;
    int num=0;
    for(int i=1;i<=k;i++){
        int a;
        cin>>a;
        if(a==1){
            int x,y;
            cin>>x>>y;
            if(st[x].size()==0) num++;
            if(st[y].size()==0) num++;
            st[x].insert(y);
            st[y].insert(x);
        }
        else{
            int x;
            cin>>x;
            if(st[x].size()!=0) num--;
            for(int t:st[x]){
                st[t].erase(st[t].find(x));
                if(st[t].size()==0) num--;
            }
            st[x].clear();
        }
        cout<<n-num<<endl;
    }
    return 0;
}




F - Merge Set

难度: ⭐⭐⭐⭐

题目大意

给定n个集合, 每个集合里面有Ai个不重复的元素, 元素的范围是1~m; 如果两个集合有相同的元素, 那么我们就可以把两个集合合并为这两个集合的并集; 请问最少进行多少次这样的操作可以让1和m处于同一个集合中;

解题思路

这题我是真没想到是用图论来解决; 对于集合和集合中的点我们可以构建一个二分图, 左边是集合(可以编号为m+1 ~ m+n), 右边是元素(1~m); 我们要找的就是从1所在集合的点到m所在集合的点之间的最短距离; 既然题目说可以让含有相同的元素的集合合并, 那我们可以把该元素作为跳板, 让集合到该元素, 然后再由该元素到另一个集合; 所以我们设集合到元素的距离为0, 而元素到集合的距离为1即可; 因为是0/1最短路, 所以直接走一遍bfs即可; 因为最开始要从1进入有1的集合, 这一步会使答案多出一个1, 最后记得减去;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 1e6 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m, res;
vector<int> v[N];
map<PII, int> mp;
int d[N];
bool st[N];
void bfs() {
    queue<int> q;
    q.push(1);
    st[1] = true;
    while (q.size()) {
        int t = q.front();
        q.pop();
        for (int x : v[t]) {
            if (!st[x]) {
                st[x] = true;
                d[x] = d[t] + mp[{t, x}];
                q.push(x);
            }
        }
    }
}
signed main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        int a, b;
        cin >> a;
        for (int j = 1; j <= a; j++) {
            cin >> b;
            v[m + i].push_back(b);
            v[b].push_back(m + i);
            mp[{m + i, b}] = 0;
            mp[{b, m + i}] = 1;
        }
    }
    bfs();
    if (st[m]) cout << d[m]-1;
    else cout << -1;
    return 0;
}
posted @ 2023-06-12 14:47  mostimali  阅读(25)  评论(0编辑  收藏  举报