Robocom训练摘记-2021RoboCom机器人开发者大赛

2021RoboCom机器人开发者大赛(初赛)

pta 题解
剩下的题目有缘再补啦~

7-1 懂的都懂

题面
image

输入样例

5 3
4 8 12 20 40
3 11 16 19
3 12 16 19
10 11 11 11 11 11 11 11 11 11 11

输出样例

Yes
No
Yes

image
思路
数据范围不是很大,直接\(O(n^4)\)暴力求解所有可能,再对所有的新图的特征数据进行判断即可

#include<bits/stdc++.h>

using namespace std;
const int maxm=200+5;
int n,k,a[maxm],b,m;

void solve(){
	cin>>n>>k;
	for(int i=0;i<n;++i){
		cin>>a[i];
	}
	map<int,int> q;
	for(int i=0;i<n-3;++i){
		for(int j=i+1;j<n-2;++j){
			for(int k=j+1;k<n-1;++k){
				for(int l=k+1;l<n;++l){
					int t=a[i]+a[j]+a[k]+a[l];
					if(t%4==0){
						q[t/4]=1;
					}
				}
			}
		}
	}
	while(k--){
		cin>>m;
		bool f=true;
		for(int i=0;i<m;++i){
			cin>>b;
			if(!q.count(b)){
				f=false;
			}
		}
		if(f) cout<<"Yes\n";
		else cout<<"No\n";
	}
	return ;
}

signed main(){
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

7-2 芬兰木棋

题面
image
image
输入样例

11
1 2 2
2 4 3
3 6 4
-1 2 2
-2 4 3
-3 6 4
-1 -2 1
-2 -4 1
-3 -6 1
-4 -8 2
2 -1 999

输出样例

1022 9

image
思路
首先从答案出发,我们可以知道,最终的分数就是所有木棋的分数和(因为分数为1的木棋一起倒和单独倒的分数一样)
那么我们要求的就是最小的次数。我们可以知道,对于所有的分数大于1的木棋都需要单独击倒;而对于分数等于1的木棋,我们则尽量连续击倒。所以我们可以对所有的木棋按照斜率归类,但是斜率不存在的这种特殊情况,所以这里所用的方法是map第一维存最简向量<x,y>,代表每一个 方向上的木棋的方向向量,map第二维是一个vector<pair<int,int>>,存木棋在方向向量长的位移量和木棋的分数。注意坐标为0的情况!此时需要划分正负输入向量
后面就是利用c++ auto遍历map,对于每一个vector我们均按照位移量先排序,统计连续段(连续p=1的段)和独立段(p>1),加到统计中即可。
另外一种做法就是,我们先预设击倒次数为n次,即每一个都需要单独击倒,之后判断是否有连续的1,每出现连续的1,则减少一次击倒。这样也可以得到最终解。
下为代码。

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;

const int maxm=1e5+5;
int n,x,y,p,c=0,ans=0;

void solve(){
    cin>>n;
    map<pair<int,int>,vector<pair<int,int>>> q;
    for(int i=0;i<n;++i){
        cin>>x>>y>>p;
		ans+=p;
    	if(x!=0 && y!=0){
			int t=__gcd(abs(x),abs(y)),xx,yy;
			xx=x/t;yy=y/t;
			q[{xx,yy}].push_back({t,p});
		}else{
			if(x){
				if(x>0)	q[{1,0}].push_back({x,p});
				else q[{-1,0}].push_back({x,p});
			}
			else{
				if(y>0) q[{0,1}].push_back({y,p});
				else q[{0,-1}].push_back({y,p});
			}
        }
	}
    for(auto a:q){
		sort(a.second.begin(),a.second.end());
    	int t=0;
    	for(int i=0;i<a.second.size();++i){
    		if(a.second[i].second<=1){
    			t=1;
			}else{
				c+=t;
				t=0;
				++c;
			}
		}
        if(t){
        	c+=t;
			t=0;
		}
	}
	cout<<ans<<' '<<c<<'\n';
	return ;
}

signed main(){
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

另一种写法:

#include<bits/stdc++.h>
#define ll long long
#define debug(x) cout << #x << " = " << x << "\n";
#define debug2(x, y) cout << #x << " = " << x << " " << #y << " = " << y << "\n"; 

using namespace std;

void solve(){
	int n;
	cin >> n;
	map<pair<int, int>, set<pair<int, int>>> f;
	for(int i = 0; i < n; ++ i){
		int x, y, p;
		cin >> x >> y >> p;
		int d = __gcd(abs(x), abs(y));
		f[{x / d, y / d}].insert({abs(d), p});
	}
	ll ans = 0, cnt = 0;
	for(auto [a, b] : f){
//		cout << a.first << " " << a.second << "\n";
		int c = 0;
		for(auto [d, p] : b){
			if(p == 1){
				++ c;
				++ ans;
			}else{
				if(c) ++ cnt;
				c = 0;
				++ cnt;
				ans += p;
			}
//			debug2(ans, cnt);
		}
		if(c) ++ cnt;
//		cout << "tot: ";
//		debug2(ans, cnt);
	}
	cout << ans << " " << cnt << '\n';
	return ;
}

signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int _ = 1;
//	cin >> _;
	while(_--){
		solve();
	}
	return 0;
}

7-3 打怪升级

题面

image
很多游戏都有打怪升级的环节,玩家需要打败一系列怪兽去赢取成就和徽章。这里我们考虑一种简单的打怪升级游戏,游戏规则是,给定有 \(N\) 个堡垒的地图,堡垒之间有道路相连,每条道路上有一只怪兽把守。怪兽本身有能量,手里的武器有价值。打败怪兽需要的能量等于怪兽本身的能量,而怪兽一旦被打败,武器就归玩家所有 —— 当然缴获的武器价值越高,玩家就越开心。

你的任务有两件:

  • 帮助玩家确定一个最合算的空降位置,即空降到地图中的某个堡垒,使得玩家从这个空降点出发,到攻下最难攻克(即耗费能量最多)的那个堡垒所需要的能量最小;

  • 从这个空降点出发,帮助玩家找到攻克任意一个其想要攻克的堡垒的最省能量的路径。如果这种路径不唯一,则选择沿途缴获武器总价值最高的解,题目保证这种解是唯一的。

输入格式:
输入第一行给出两个正整数 \(N (≤1000)\)\(M\),其中 \(N\) 是堡垒总数,\(M\) 是怪兽总数。为简单起见,我们将堡垒从 \(1\)\(N\) 编号。随后 \(M\) 行,第 \(i\) 行给出了第 \(i\) 只怪兽的信息,格式如下:

B1 B2 怪兽能量 武器价值

其中 B1B2 是怪兽把守的道路两端的堡垒编号。题目保证每对堡垒之间只有一只怪兽把守,并且 怪兽能量武器价值 都是不超过 \(100\) 的正整数。

再后面是一个正整数 \(K(≤N)\) 和玩家想要攻克的 \(K\) 个目标堡垒的编号。

输出格式:
首先在一行中输出玩家空降的堡垒编号 \(B0\)。如果有多种可能,则输出编号最小的那个。

随后依次为玩家想要攻克的每个堡垒 \(B\) 推荐最省能量的攻克路径,并列出需要耗费的能量值和沿途缴获武器的总价值。注意如果最省力的路径不唯一,则选择沿途缴获武器总价值最高的解。格式为:

B0->途经堡垒1->...->B
总耗费能量 武器总价值

输入样例:

6 12
1 2 10 5
2 3 16 20
3 1 4 2
2 4 20 22
4 5 2 2
5 3 12 6
4 6 8 5
6 5 10 5
6 1 20 25
1 5 8 5
2 5 2 1
2 6 8 5
4
2 3 6 5

输出样例:

5
5->2
2 1
5->1->3
12 7
5->4->6
10 7
5
0 0

image

思路

先跑一遍 floyd 确定空降位置,即距离剩下所有堡垒中能量花费最大值的最小位置

第二个所求问题就变为了单源最短路问题,跑 dij ,记录转移点即可

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define debug(x) cout << #x << " = " << x << "\n";
#define debug2(x, y) cout << #x << " = " << x << " " << #y << " = " << y << "\n"; 

using namespace std;
typedef pair<int, int> pii;
// 先 floyd 再 dij
void solve(){
	const int inf = 0x3f3f3f3f;
	int n, m;
	cin >> n >> m;
	vector e(n + 1, vector< pair<int, pair<int, int>> >());
	vector dis(n + 1, vector<int>(n + 1, inf));
	for(int i = 0; i < m; ++ i){
		int u, v, w, val;
		cin >> u >> v >> w >> val;
		dis[u][v] = dis[v][u] = w;
		e[u].push_back({v, {w, val}});
		e[v].push_back({u, {w, val}});
	}
	for(int l = 1; l <= n; ++ l){
		for(int i = 1; i <= n; ++ i){
			for(int j = 1; j <= n; ++ j){
				if(i == j || i == l || j  == l) continue;
				dis[i][j] = min(dis[i][j], dis[i][l] + dis[l][j]);
			}
		}
	}
	
	int pos = 0, nen = inf;
	for(int i = 1; i <= n; ++ i){
		int mx = 0;
		for(int j = 1; j <= n; ++ j){
			if(i == j) continue;
			mx = max(mx, dis[i][j]);
		}
		if(mx < nen){
			pos = i;
			nen = mx;
		}
//		debug2(i, mx);
	}
//	debug(pos);
	cout << pos << "\n";

	vector<int> pre(n + 1), newdis(n + 1, inf), vis(n + 1), vals(n + 1);
	priority_queue<pii, vector<pii>, greater<pii>> q;
	q.push({0, pos});
	newdis[pos] = 0;
	while(q.size()){
		auto [__, u] = q.top();
		q.pop();
		if(vis[u]) continue;
		// debug2(u, newdis[u]);
		vis[u] = true;
		for(auto [v, a] : e[u]){
			auto [w, val] = a;
			if(!vis[v] && (newdis[v] > newdis[u] + w || newdis[v] == newdis[u] + w && vals[v] < vals[u] + val)){
				newdis[v] = newdis[u] + w;
				vals[v] = vals[u] + val;
				pre[v] = u;
				q.push({newdis[v], v});
			}
		}
	}
	
	int k;
	cin >> k;
	for(int i = 0; i < k; ++ i){
		int top;
		cin >> top;
		int temp = top;
		string ans;
		while(temp != pos){
			ans = "->" + to_string(temp) + ans;
			temp = pre[temp];
		}
		ans = to_string(pos) + ans;
//		debug(top);
		cout << ans << "\n"
			 << newdis[top] << " " << vals[top] << "\n";
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int _ = 1;
//	cin >> _;
	while(_--){
		solve();
	}
	return 0;
}

摘记:仅利用 floyd 算法求解该问题
https://blog.csdn.net/weixin_74229477/article/details/131017608

7-4 疫情防控

题面

疫情尚未结束,严防疫情反复。为了做好疫情防控工作,国内设置了地区风险等级,对于中高风险地区的人员采取限制移动、居家隔离等手段。

为了研究疫情防控对于跨地区交通运输的影响,假设现在有 \(N\) 个机场,\(M\) 条航线,每天都会新增一个防控地区,一个防控地区会导致一个机场无法正常运作,航线也自然无法正常运行,每天会有 \(Q_i\) 对旅客从 \(X_i\) 机场前往 \(Y_i\) 机场,请计算有多少对旅客会受到影响无法完成行程。

旅客只要能直达或通过若干次中转,且乘坐的所有航线的出发和到达机场都正常运作,即视作可完成行程。

输入格式:
输入第一行是三个整数 \(N,M,D (1≤N≤5×10^4, 1≤M≤2×10^5, 1≤D≤10^3)\), 表示机场数、航线数以及新增防控地区的天数。

接下来首先有 \(M\) 行,每行给出空格分隔的两个数字 \(A\)\(B\),表示编号为 \(A\)\(B\) 的机场之间有一条航线。航线是双向的,机场编号从 \(1\)\(N\)

然后是 \(D\) 块输入,每块输入内第一行为空格分隔的两个整数 \(C\)\(Q(1≤Q≤10^3)\),表示新增机场编号为 \(C\) 所在的城市为防控地区,今天有 \(Q\) 段行程。数据保证新增的城市之前一定不是防控地区。

接下来的 \(Q\) 行,每行是空格分隔的两个数字 \(X\)\(Y\),表示编号为 \(X\)\(Y\) 的机场的一段行程。行程有可能包括之前就已经成为防控地区的城市。

输出格式:
对于每天的询问,请在一行中输出在新增了一个防控地区后当天的行程有多少不能成行。

输入样例:

5 5 3
1 2
1 3
1 5
2 5
3 4
4 3
1 3
1 4
2 3
5 3
3 4
2 3
3 5
1 3
2 3
2 5
3 4

输出样例:

1
2
3

image

思路

可以发现,正向求解较为困难,我们考虑离线处理问题

先把所有询问存下来,将合法点和边都用并查集统计。

从后往前处理,先处理询问,再将当天的封控城市所连的合法边加入到并查集中去,依次处理即可得出答案

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define debug(x) cout << #x << " = " << x << "\n";
#define debug2(x, y) cout << #x << " = " << x << " " << #y << " = " << y << "\n"; 

using namespace std;
typedef pair<int, int> pii;
// 逆序处理问题,利用并查集快速判断连通性
struct DSU{
	int num;
	vector<int> fa, sz;
	DSU(int x = 1e5) : num(x), fa(x + 1), sz(x + 1, 1){
		for(int i = 0; i <= x; ++ i) fa[i] = i;
	}
	int findfa(int x){
		while(x != fa[x]) x = fa[x] = fa[fa[x]];
		return x;
	}
	int size(int x){ return sz[findfa(x)]; }
	bool same(int x, int y){ return findfa(x) == findfa(y); }
	bool merge(int x, int y){
		x = findfa(x); y = findfa(y);
		if(x == y) return false;
		if(sz[x] < sz[y]) swap(x, y);
		fa[y] = x;
		sz[x] += sz[y];
		return true;
	}
};

void solve(){
	int n, m, d;
	cin >> n >> m >> d;
	vector e(n + 1, vector<int>());
	for(int i = 0; i < m; ++ i){
		int a, b;
		cin >> a >> b;
		e[a].push_back(b);
		e[b].push_back(a);
	}
	DSU dsu(n);
	vector query(d, vector<pair<int, int>>());
	vector<int> c(d), vis(n + 1);
	for(int i = 0; i < d; ++ i){
		int q;
		cin >> c[i] >> q;
		vis[c[i]] = 1;
		while(q --){
			int x, y;
			cin >> x >> y;
			query[i].push_back({x, y});
		}
	}
	for(int i = 1; i <= n; ++ i){
		if(!vis[i]){
			for(auto v : e[i]){
				if(!vis[v])
					dsu.merge(i, v);
			}
		}
	}
	for(int i = d - 1; i >= 0; -- i){
		int id = c[i], ans = 0;
		for(auto [x, y] : query[i]){
			if(!dsu.same(x, y)){
				++ ans;
			}
		}
		c[i] = ans;
		vis[id] = 0;
		for(auto v : e[id]){
			if(!vis[v])
				dsu.merge(id, v);
		}
	}
	for(int i = 0; i < d; ++ i) cout << c[i] << "\n";
	return ;
}

signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int _ = 1;
//	cin >> _;
	while(_--){
		solve();
	}
	return 0;
}

2021RoboCom机器人开发者大赛(复赛)

pta 题解

7-1 冒险者分队

题面
image
image

输入样例

4
25 30 35
65 10 15
100 200 300
200 180 220
100 100 100
0 0 0
777 888 999
777 888 999

输出样例

1
3
-1
0

image
思路
详可见 pta 题解
第一步是判断存在性。存在性可以通过三种方式来判断:

  • 前后差值和是否为0
  • 是否有差值不是20的倍数
  • 是否所有处理后的差值模 3 的结果均相等

第二步是寻找最小操作数(进行第二步的前提是,第一步均符合)
预先处理3个差值,差值一正一负一零或一正两负或一负两正。前两种情况一样,最后一种可以直接全体取负转化成第二种情况
对于操作,尽可能先使用(1,1,-2)使得正数趋向于0,再利用(1,1,-2)和(2,-1,-1)形成的(3,0,-3)进行最后的操作即可
详可见pta题解和代码上面表述不详细

#include<bits/stdc++.h>
#define ll long long 

using namespace std;
const int maxm=2e5+5;
ll a[3],b[3],c[3],ans;

void solve(){
	for(int i=0;i<3;++i){
		cin>>a[i];
	}
	ll sum=0;
	bool f=false;
	for(int i=0;i<3;++i){
		cin>>b[i];
		c[i]=b[i]-a[i];
		if(abs(c[i])%20) f=true;
		sum+=c[i];
		c[i]/=20;
	}
	int p=abs(c[0]%3+3)%3;
	if(abs(3+c[1]%3)%3!=p || abs(c[2]%3+3)%3!=p) f=true;
	if(f||sum) ans=-1;
	else{
		sort(c,c+3);
		if(c[0]*c[1]<0) ans=min(abs(c[1]),abs(c[2])),p=0;
		else ans=min(abs(c[0]),abs(c[1])),p=2;
		if(p==0) c[p]=-c[p];
		c[p]-=2*ans;
		if(c[p]){
			ans+=c[p]/3*2;
		}
	}
	cout<<ans<<'\n';
	return ;
}

signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int _=1;
	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

7-2 拼题A打卡奖励

题面
image
输入样例

5 110
70 10 20 50 60
28 1 6 18 22

输出样例

40

image
题意:
普通背包问题。你有 m 的时间可以做 n 张打卡卷,做完第 i 张打卡卷需要 \(m_i\) 的时间,做完可以获得 \(c_i\) 的金币。问你你在 m 时间里能获得的最大金币数
思路:
利用普通背包问题的求解过程超时,,,因为m太大了!
image
怪,不知道普通背包问题怎么优化了。。。

做法:对金币数量跑01背包

void solve(){
	const int inf = 0x3f3f3f3f;
	int n, m, ans = 0;
	cin >> n >> m;
	vector<int> t(n), c(n);
	for(int i = 0; i < n; ++ i) cin >> t[i];
	for(int i = 0; i < n; ++ i) cin >> c[i];
	vector<int> dp(n * 30 + 1, inf);
	dp[0] = 0;
	for(int i = 0; i < n; ++ i){
		for(int j = n * 30 + 1; j >= c[i]; -- j){
			if(dp[j - c[i]] != inf && dp[j - c[i]] + t[i] <= m){
				dp[j] = min(dp[j], dp[j - c[i]] + t[i]);
				ans = max(ans, j);
			}
		}
	}
	cout << ans << '\n';
	return ;
}

下面的做法仅供图一乐

训练的时候用了一种奇怪的做法过了,这里摘记一下
就是对于每一张试卷存一个金币数与花费时间的比值,再按照这个比值从大到小排序,利用尺取法求解答案。
还是感觉这个做法很怪?

#include<bits/stdc++.h>
#define ll long long 

using namespace std;
const int maxm=2e5+5;
ll n,m;
struct node{
	ll min,c;
	double t;
}p[maxm];

void solve(){
	cin>>n>>m;
	for(int i=0;i<n;++i){
		cin>>p[i].min;
	}
	for(int i=0;i<n;++i){
		cin>>p[i].c;
		p[i].t=1.0*p[i].c/p[i].min;
	}
	sort(p,p+n,[](node x,node y){
		return x.t>y.t;
	});
	int cnt=0,sum=0,ans=0,l=0,r=0;
	while(l<n&&r<n){
		if(cnt+p[r].min<=m){
			cnt+=p[r].min;
			sum+=p[r].c;
			if(sum>ans){
				ans=sum;
			}
			++r;
		}else{
			cnt-=p[l].min;
			sum-=p[l].c;
			++l;
		}
	}
	cout<<ans<<'\n';
	return ;
}

signed main(){
	int _=1;
	// cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

2021RoboCom机器人开发者大赛(决赛)

pta 题解

7-1 绿地围栏

题面
image
image
输入样例

14 5
2 1 1 4 3 5 2 6 -1 4 0 2 -1 0

输出样例

0 0
2 1
5 3
6 -1
2 0

思路
依着围栏走,数据范围不大直接暴力,同时记一个走过的路长len,当len%L=0时需要打桩。注意一点:题目只给了拐点的简化坐标,但是最后一个拐点到原点之间路上是否需要打桩别忘了判断。

#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<" "<<x<<'\n';
#define debug2(x,y) cout<<#x<<" "<<x<<" "<<#y<<" "<<y<<'\n';


const int maxm=2e5+5;
int n,l;
vector<pair<int,int>> q;

void solve(){
	cin>>n>>l;
	int t=0,a[3]={0,0,0},p,c,f;
	q.push_back({0,0});
	for(int i=0;i<n;++i){//0 change y 1 change x
		cin>>c;
		if(i%2) p=0;
		else p=1;
		if(c>a[p]) f=1;
		else f=-1;
		for(int j=a[p]+f;j*f<=c*f;j+=f){
			++t;
			if(t%l==0){
				if(i%2)
					q.push_back({j,a[1]});
				else q.push_back({a[0],j});
			}
		}
		a[p]=c;
	}
	c=0;p=1-n%2;
	if(c>a[p]) f=1;
	else f=-1;
	for(int j=a[p]+f;j*f<=c*f;j+=f){
		++t;
		if(t%l==0){
			if(n%2)
				q.push_back({j,a[1]});
			else q.push_back({a[0],j});
		}
	}
	pair<int,int> dd;
	if(q.size()>1){
		dd=q.back();
		if(dd.first==0&&dd.second==0){
			q.pop_back();
		}
	}
	int len=q.size();
	for(int i=0;i<len;++i){
		cout<<q[i].first<<' '<<q[i].second<<'\n';
	}
	return ;
}

signed main(){
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

7-3 账户安全预警

题面
image
image
输入样例1

24 3 4
daohaole@qq.com 218.109.231.189
1jiadelaolao@163.com 112.192.203.187
chenyuelaolao@zju.edu.cn 112.18.235.143
jiadelaolao@163.com 112.192.203.187
chenyuelaolao@zju.edu.cn 113.18.235.143
jiadelaolao@163.com 111.192.203.187
daohaole@qq.com 218.109.231.189
chenyuelaolao@zju.edu.cn 111.18.235.143
1jiadelaolao@163.com 115.192.203.187
daohaole@qq.com 113.189.58.141
1jiadelaolao@163.com 111.192.203.187
daohaole@qq.com 112.18.58.145
1jiadelaolao@163.com 114.192.203.187
chenyuelaolao@zju.edu.cn 112.18.235.143
daohaole@qq.com 123.89.158.214
chenyuelaolao@zju.edu.cn 112.18.235.143
youdaohaole@qq.com 218.109.231.189
jiadelaolao@163.com 113.192.203.187
youdaohaole@qq.com 218.109.231.189
jiadelaolao@163.com 114.192.203.187
youdaohaole@qq.com 113.189.58.141
youdaohaole@qq.com 123.89.158.214
1jiadelaolao@163.com 113.192.203.187
youdaohaole@qq.com 112.18.58.145

输出样例1

1jiadelaolao@163.com
111.192.203.187 1
112.192.203.187 1
113.192.203.187 1
114.192.203.187 1
115.192.203.187 1
daohaole@qq.com
218.109.231.189 2
112.18.58.145 1
113.189.58.141 1
123.89.158.214 1
youdaohaole@qq.com
218.109.231.189 2
112.18.58.145 1
113.189.58.141 1
123.89.158.214 1

输入样例2

24 5 8
daohaole@qq.com 218.109.231.189
1jiadelaolao@163.com 112.192.203.187
chenyuelaolao@zju.edu.cn 112.18.235.143
jiadelaolao@163.com 112.192.203.187
chenyuelaolao@zju.edu.cn 113.18.235.143
jiadelaolao@163.com 111.192.203.187
daohaole@qq.com 218.109.231.189
chenyuelaolao@zju.edu.cn 111.18.235.143
1jiadelaolao@163.com 115.192.203.187
daohaole@qq.com 113.189.58.141
1jiadelaolao@163.com 111.192.203.187
daohaole@qq.com 112.18.58.145
1jiadelaolao@163.com 114.192.203.187
chenyuelaolao@zju.edu.cn 112.18.235.143
daohaole@qq.com 123.89.158.214
chenyuelaolao@zju.edu.cn 112.18.235.143
youdaohaole@qq.com 218.109.231.189
jiadelaolao@163.com 113.192.203.187
youdaohaole@qq.com 218.109.231.189
jiadelaolao@163.com 114.192.203.187
youdaohaole@qq.com 113.189.58.141
youdaohaole@qq.com 123.89.158.214
1jiadelaolao@163.com 113.192.203.187
youdaohaole@qq.com 112.18.58.145

输出样例2

1jiadelaolao@163.com
111.192.203.187 1
112.192.203.187 1
113.192.203.187 1
114.192.203.187 1
115.192.203.187 1

思路
利用STL维护多关键字排序。。。反正就是个大水题,大模拟题。。。
代码很丑,勿怪!

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(x) cout<<#x<<" "<<x<<'\n';
#define debug2(x,y) cout<<#x<<" "<<x<<" "<<#y<<" "<<y<<'\n';


const int maxm=1e4+5;
int n,tip,tin,pos,sscnt;
string ss,tt;
map<string,pair<set<string>,int>> q;
map<string,map<string,int>> cnt;
struct node{
	string dd;
	int c;
}p[maxm],ans[maxm],all[maxm];

void solve(){
	cin>>n>>tip>>tin;
	for(int i=0;i<n;++i){
		cin>>ss>>tt;
		if(q.count(ss)){
			q[ss].first.insert(tt);
			++cnt[ss][tt];
			++q[ss].second;
		}else{
			q[ss].first.insert(tt);
			q[ss].second=1;
			cnt[ss][tt]=1;
		}
	}
	bool f=true;
	sscnt=0;
	pos=0;
	int len=0;
	for(auto a:q){
		all[sscnt].c=a.second.first.size();
		all[sscnt].dd=a.first;
		++sscnt;
		if(a.second.first.size()>tip&&a.second.second>tin){
			ans[len].dd=a.first;
			ans[len].c=a.second.first.size();
			++len;
		}
	}
	sort(ans,ans+len,[](node x,node y){
		return x.c>y.c||x.c==y.c&&x.dd<y.dd;
	});
	for(int i=0;i<len;++i){
		f=false;
		cout<<ans[i].dd<<'\n';
		pos=0;
		for(auto xx:q[ans[i].dd].first){
			p[pos].dd=xx;p[pos].c=cnt[ans[i].dd][xx];
			++pos;
		}
		sort(p,p+pos,[](node x,node y){
			return x.c>y.c||x.c==y.c&&x.dd<y.dd;
		});
		for(int i=0;i<pos;++i){
			cout<<p[i].dd<<" "<<p[i].c<<'\n';
		}
	}
	if(f){
		sort(all,all+sscnt,[](node x,node y){
			return x.c>y.c||x.c==y.c&&x.dd<y.dd;
		});
		int num=all[0].c;
		for(int i=0;i<sscnt;++i){
			if(all[i].c!=num) break;
			cout<<all[i].dd<<'\n';
			pos=0;
			for(auto xx:q[all[i].dd].first){
				p[pos].dd=xx;p[pos].c=cnt[all[i].dd][xx];
				++pos;
			}
			sort(p,p+pos,[](node a,node b){
				return a.c>b.c||a.c==b.c&&a.dd<b.dd;
			});
			for(int i=0;i<pos;++i){
				cout<<p[i].dd<<" "<<p[i].c<<'\n';
			}
		}
	}
	return ;
}

signed main(){
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}
posted on 2023-07-14 16:08  Qiansui  阅读(39)  评论(0编辑  收藏  举报