Robocom本科组初赛

这几天主要在写robocom的题,写了个初赛就自闭了,我怎么可以这么菜啊。

7-1 懂的都懂(20)

题意:设n个非负整数和k次询问,每一次询问中的m个数字是否都是n个非负整数中其中四个数字的平均值,是的话输出Yes,否则输出No。

题目特征:n个数中选4个数,并且要对这个结果进行处理。

思路:dfs,搜到四个数就停下来,把平均值用map存起来,记得要回溯。我写代码的时候采用的是固定一个数,然后去搜剩下的三个数,但其实搜四个数也是一样的。(第一反应居然是全排列,蓝桥杯打傻了

1.	#include<bits/stdc++.h>  
2.	using namespace std;  
3.	int n, k, a[55], vis[55];  
4.	map<int, int> mp;  
5.	void dfs(int num, int sum, int kk){  
6.	    if(num == 4){  //选够四个数
7.	        if(sum % 4 == 0)  mp[sum / 4] = 1;  //因为输进来的都是整数,所以确定模4等于0之后才能存入
8.	        return;  
9.	    }  
10.	    vis[kk] = 1;  
11.	    for(int i = 0; i < n; i++){  
12.	        if(!vis[i]){  
13.	            dfs(num + 1, sum + a[i], i);  
14.	        }
15.	    }
16.	    vis[kk] = 0;  //回溯
17.	}  
18.	int main(){  
19.	    ios::sync_with_stdio(false);  
20.	    cin.tie(0);  
21.	    cin >> n >> k;  
22.	    int kk = 0;  
23.	    for(int i = 0; i < n; i++){  
24.	        cin >> a[i];  
25.	    }  
26.	    sort(a, a + n);  
27.	    for(int i = 0; i < n; i++){  
28.	        dfs(1, a[i], i);  
29.	    }  
30.	//  for(auto it = mp.begin(); it != mp.end(); it++){  
31.	//      printf("****%d\n", (*it).first);  
32.	//  }  
33.	    for(int i = 0; i < k; i++){  
34.	        int flag = 0;  
35.	        int x;  cin >> x;  
36.	        for(int j = 0; j < x; j++){  
37.	            int g;  cin >> g;  
38.	            if(!mp[g])  flag = 1;  
39.	        }  
40.	        if(!flag)  printf("Yes\n");  
41.	        else  printf("No\n");  
42.	    }  
43.	    return 0;  
44.	}  

题意:假设现在有 N 个机场,M 条航线,每天都会新增一个机场无法正常运作,每天会有 Q i对旅客从 X i机场前往 Y i机场,请计算有多少对旅客会受到影响无法完成行程。

思路:并查集+反向建图,最开始写的是dfs,只过了部分测试点,看了题解之后直呼妙啊,先把所有变量存下来,然后从后面的点开始建图,因为点是从前向后一个一个消失的,建立一个点相当于该点的前一个点消失后的状态(太妙了。在此之前还要对没有被防控的机场建图。

代码:

#include<bits/stdc++.h>
using namespace std;
struct node{
	int st, ed;
};
int fa[50005], vis[50005];
int n, m, d;  
vector<vector<node> > v(50005);
vector<node> vn;
vector<int> ans;
stack<int> sta;
void init(){
	for(int i = 0; i <= n; i++)  fa[i] = i;
}
int find(int x){
	if(x == fa[x])  return x;
	else  return fa[x] = find(fa[x]);
}
int main(){
	cin >> n >> m >> d;
	init();
	for(int i = 0; i < m; i++){
		int a, b;  cin >> a >> b;
		vn.push_back({a, b});
	}
	for(int i = 0; i < d; i++){
		int c, q;  cin >> c >> q;
		sta.push(c);
		vis[c] = 1;
		for(int j = 0; j < q; j++){
			int x, y;  cin >> x >> y;
			v[c].push_back({x, y});
		}
	}
	for(int i = 0; i < m; i++){
		if(vis[vn[i].st] || vis[vn[i].ed])  continue;
		int finda = find(vn[i].st);
		int findb = find(vn[i].ed);
		if(finda != findb){
			fa[finda] = findb;
		}
	}
	while(!sta.empty()){
		int qf = sta.top(), cnt = 0;
		sta.pop();
		for(int i = 1; i <= n; i++)  int ww = find(i);
		for(int i = 0; i < v[qf].size(); i++){
			if((fa[v[qf][i].st] != fa[v[qf][i].ed]) || vis[v[qf][i].st] || vis[v[qf][i].ed])  cnt++;
		}
		for(int i = 0; i < m; i++){
			if(vn[i].st != qf && vn[i].ed != qf)  continue;
			int finda = find(vn[i].st);
			int findb = find(vn[i].ed);
			if(finda != findb){
				fa[finda] = findb;
			}
		}
        vis[qf] = 0;
        ans.insert(ans.begin(), cnt);
	}
	for(int i = 0; i < ans.size(); i++){
        printf("%d\n", ans[i]);
    }
}

 7-3 打怪升级

 

 

这一题没有全部过完(23/25)不知道是哪里出了问题,看好多人都是用迪杰斯特拉去路径还原,而我搜了一个弗洛伊德的路径还原板子,照着写上去了,但是没有全过。

题意:找一个点到输入的所有点耗费的能量最小,并且沿途获得的武器价值最大。

思路:典型的最短路问题,数据范围很小,考虑使用floyd,并且不仅要记录耗费的能量,还要记录获得的价值,路径能量耗费相同的情况下选择获得价值高的,还有最短路的路径还原问题。

路径还原参照博客:(36条消息) floyd——记录路径,原理详解。_小酒窝.的博客-CSDN博客_floyd算法怎么记录路径

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
struct node{
	int me, wv;    //武器价值和怪兽能量
};
int pre[1005][1005];   //记录路径还原
int n, m;
node g[1005][1005];   //记录图
int num[1005];    //要攻破的堡垒

void print(int i,int j)    //路径还原的输出
{
    if(i==j) return ;
    if(pre[i][j]==0) cout << "->" << j;
    else{
        print(i,pre[i][j]);
        print(pre[i][j],j);
    }
}
int main(){
	cin >> n >> m;
	memset(g, inf, sizeof(g));
	memset(pre, 0, sizeof(pre));
	for(int i = 0; i < m; i++){
		int b1, b2, x, y;  cin >> b1 >> b2 >> x >> y;
		g[b1][b2] = g[b2][b1] = {x, y};
	}
	for(int k = 1; k <= n; k++){
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= n; j++){
				if(g[i][k].me + g[k][j].me < g[i][j].me)
				{
					pre[i][j]=k;
					g[i][j].me = g[i][k].me + g[k][j].me;
					g[i][j].wv = g[i][k].wv + g[k][j].wv;
				}
				else if(g[i][k].me + g[k][j].me == g[i][j].me){
					if((g[i][k].wv + g[k][j].wv > g[i][j].wv) && (g[i][k].wv + g[k][j].wv < inf ))  
					{
						pre[i][j]=k;
						g[i][j].wv = g[i][k].wv + g[k][j].wv;
					}
				}
			}
		}
	}
	int k; cin>>k;
	for(int i=0;i<k;i++) cin>>num[i];
	int ans=inf;
	int ans1;
	for(int i=1;i<=n;i++)
	{
		int maxx=-1;
		for(int j=0;j<k;j++)
		{
			int dis=g[i][num[j]].me;
			maxx=max(maxx,dis);
		}
		if(ans>maxx)
		{
			ans=maxx;
			ans1=i;
		}
		
	}
	cout<<ans1<<endl;
	for(int i = 0; i < k; i++){
		printf("%d", ans1);
        print(ans1,num[i]);
        printf("\n");
        if(ans1 == num[i])  printf("0 0\n");
        else  printf("%d %d\n", g[ans1][num[i]].me, g[ans1][num[i]].wv);
	}
}

7-2芬兰木棋(25分)

芬兰木棋(Mölkky,又称芬兰木柱)是源自芬兰的一项运动。哲哲将这个运动改造成了赛博朋克单人版,现在场上一开始有 N 根立起的小木棋(上面分别标有一个非负整数),哲哲投掷一根大木棋去击倒这些小木棋以获得分数。分数规则如下:

  • 如果仅击倒 1 根木棋,则得木棋上的分数。
  • 如果击倒 2 根或以上的木棋,则只得击倒根数的分数。(例如击倒 5 根,则得 5 分。)

哲哲固定站在 (0,0) 点上,四周放着若干个小木棋 (Xi​,Yi​),坐标均为整数。每次哲哲可以朝一个方向扔出大木棋,大木棋会打倒这个方向上离哲哲最近的 k 个小木棋。哲哲游戏水平很高超,所以这个 k 可以自由控制。

请问哲哲最多能拿多少分,在获得最多分数的情况下最少需要扔出多少次大木棋?

规则与真实规则有较大出入,真实游玩时请以国际莫尔基组织的规则为准

输入格式:

输入第一行是一个正整数 N (1 ≤ N ≤ 105),表示场上一开始有 N 个木棋。

接下来 N 行,每行 3 个整数 Xi​,Yi​,Pi​,分别表示木棋放置在 (Xi​,Yi​),木棋上的分数是 Pi​。坐标在 32 位整数范围内,分数为小于等于 1000 的正整数。

保证 (0,0) 点没有木棋,也没有木棋重叠放置。

输出格式:

输出一行两个数,表示最多分数以及获得最多分数最少需要投掷大木棋多少次。

输入样例:

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

题意:如题,这道题题意还是比较好理解的。

思路:计算斜率,对斜率相同的点存进一个容器里,然后计算有多少个连续的1,若不是1,那就直接加上去,这里要注意的是,斜率相同,有可能象限不同,所以还要分象限,我对每个容器都加了个0,0进行隔断。

今天学到了个额外的东西,就是自定义结构体内嵌比较器一定要写清楚,清楚到足以区分每个结构体,因为有些容器的排序是完全看你的自定义结构体内嵌比较器,如果不能够区分清楚,那电脑就会认为它们是同一个东西。

#include<bits/stdc++.h>
#define int long long
using namespace std;
struct node{
	int x, y, p;
};
bool cmp(node a, node b){
	if(a.x != b.x)  return a.x < b.x;
	else  return a.y < b.y;
}
signed main(){
    int n, ans1 = 0, ans2 = 0;  cin >> n;
    map<double, vector<node> > mp;
    for(int i = 0; i < n; i++){
    	int xx, yy, pp;  cin >> xx >> yy >> pp;
    	ans1 += pp;
    	if(mp[xx * 1.0 / yy].size() == 0){
    		node a;
    		a.x = 0, a.y = 0, a.p = 0;
    		mp[xx * 1.0 / yy].push_back(a);
		}
		node b;
		b.x = xx, b.y = yy, b.p = pp;
		mp[xx * 1.0 / yy].push_back(b);
	}
	for(auto it = mp.begin(); it != mp.end(); it++){
		vector<node> v = (*it).second;
		sort(v.begin(), v.end(), cmp);
		int index = -1;
		for(int i = 0; i < v.size(); i++){
			if(v[i].p == 0)  continue;
			else if(v[i].p != 1)  ans2++;
			else{
				if(i == v.size() - 1 || v[i + 1].p != 1)  ans2++;
			}
		}
	}
	printf("%lld %lld\n", ans1, ans2);
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值