2022SCUT Winter Training Day 1 记录(基础专题)

A - Prime Ring Problem

原链接挂了,有个洛谷的链接:素数环 Prime Ring Problem - 洛谷

一开始想到的是全排列,但是复杂度是n!,不可行。所以改用dfs(因为dfs可加入剪枝)

#include <bits/stdc++.h>
using namespace std;
int t,a[20],n,p[40], vis[20];
void init(){ //打素数表(打到39就能停了)
	p[2]=p[3]=p[5]=p[7]=p[11]=p[13]=p[17]=p[19]=p[23]=p[29]=p[31]=1;
	a[1] = 1; vis[1] = 1;//第一个位置必须是1,那么1已经用过了
}
void dfs(int x){
	if(x==n+1 && p[a[n]+a[1]]){
		for(int i=1;i<n;i++) cout<<a[i]<<' ';
		cout<<a[n]<<'\n';
	}
	if(x==n+1) return;
	for(int i=2;i<=n;i++){
		if(vis[i] || !p[i+a[x-1]]) continue;
		a[x]=i; vis[i]=1;
		dfs(x+1);
		vis[i]=0;
    }
}
int main(){
    init();
	while(cin>>n){
		if(t) cout<<'\n';
		printf("Case %d:\n",++t);
		dfs(2);
	}
	return 0;
}

B - Catch That Cow  

3278 -- Catch That Cow

一开始想用贪心,但后来发现并不可行。本题要用bfs,需要注意几处剪枝:

  1. 起点坐标比终点大,只能后退,步数直接输出n-k
  2. 遍历过的地方避免重复遍历,加入vis数组进行剪枝

另外,还要注意走到的地方不能越界。

#include <iostream>
#include <queue>
using namespace std;
int n, k, cnt;
bool vis[1000005];

int main(){
    cin>>n>>k;
    if(k<n) {cout<<n-k<<endl; return 0;}
    queue<int> q;
    q.push(n);
    while(q.size()){
        int size = q.size();
        while(size--){
            int c = q.front(); q.pop();
            //
            if(vis[c]) continue;
            if(0<c && c<100005) vis[c] = 1;
            //
            if(c==k) {cout<<cnt; return 0;}
            else{
                if(0<c-1 && c-1<100005) q.push(c-1);
                if(0<c+1 && c+1<100005) q.push(c+1);
                if(0<2*c && 2*c<100005) q.push(2*c);
            }
        }
        cnt++;
    }
}

 C - Cinema 

Problem - 670C - Codeforces

用map记录认识每种语言的科学家的人数,然后O(N)把所有电影扫一遍即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
const int maxn = 200005;
int n, m, tmp; //科学家数量,电影数量
int a[maxn], b[maxn], max1, max2, id = 1; //注意id初始必须设为1,否则会出现结果是0的错误答案
map<int, int> mp;
int main(){
	scanf("%d",&n);
	for(int i=1; i<=n; i++) {scanf("%d", &tmp); mp[tmp]++;} //离散化记录
	scanf("%d",&m);
	for(int i=1; i<=m; i++) scanf("%d", &a[i]);
	for(int i=1; i<=m; i++){
		scanf("%d", &b[i]);
		if(mp[a[i]] > max1 || (mp[a[i]] == max1 && mp[b[i]] > max2)){
			id = i;
			max1 = mp[a[i]];
			max2 = mp[b[i]];
		}
	}
	cout<<id;
}

G - Restorer Distance 

Problem - 1355E - Codeforces

设定的高度如果很高或者很低,需要的操作次数都很大,而高度在中间的时候则小一些,满足单峰性,用三分答案解决。注意可以预处理移动砖块的成本为 min(移动一块,加一块+减一块)。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e5+5;
int n, a, r, m, arr[maxn], lb=1e9, rb=-1e9;

int func(int x){
    int low=0, up=0, res = 0;
    for(int i=1; i<=n; i++){
        if(arr[i] < x) low += (x-arr[i]);
        else up += (arr[i]-x);
    }
    //1.移动
    int move = min(up, low);
    res += move*m;
    //2.加或者减
    res += (low-move)*a;
    res += (up-move)*r;

    return res;
}
signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin>>n>>a>>r>>m;
    m=min(m,a+r);//预处理
    for(int i=1; i<=n; i++){
        cin>>arr[i];
        lb = min(arr[i], lb); rb = max(arr[i], rb);
    }
    while(lb < rb){
        int m1 = lb+(rb-lb)/3, m2 = rb-(rb-lb)/3;
        if(func(m1) <= func(m2)) rb = m2-1;
        else lb = m1+1;
    }
    cout<<func(lb);
}

J - Meteor Shower

3669 -- Meteor Shower

看起来好像有点难,实际上就是一个bfs,用二维数组记录流星落下时间和安全位置即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 400;
const int dx[4]={1,-1,0,0}, dy[4]={0,0,1,-1};
bool vis[maxn][maxn]; //优化:遍历过的点不再遍历
int m, mp[maxn][maxn];

void bfs(){
	if(mp[0][0]==0) {cout<<-1; return;}
	if(mp[0][0]==-1) {cout<<0; return;}
	int t = 0;
	queue<pair<int,int> > q;
	q.push(make_pair(0,0));
	while(q.size()){
		t++;
		int size = q.size();
		while(size--){
			pair<int,int> P = q.front(); q.pop();
			int x=P.first, y=P.second;
			if(vis[x][y]) continue; //之前遍历过了,跳过
			vis[x][y] = 1;
			for(int i=0; i<4; i++){
				int xi=x+dx[i], yi=y+dy[i];
				if(xi<0 || yi<0) continue; //跳过越界格子
				if(mp[xi][yi]==-1) {cout<<t; return;} //找到安全地方,结束
				if(t>=mp[xi][yi]) continue; //被破坏了,跳过
				q.push(make_pair(xi,yi));
			}
		}
	}
	cout<<-1;
}
int main(){
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin>>m;
	int x,y,t;
	memset(mp,-1,sizeof(mp)); //init
	for(int i=1; i<=m; i++){
		cin>>x>>y>>t;
		mp[x][y] = (mp[x][y]==-1? t : min(mp[x][y], t)); //注意取最小
		// if(mp[x][y] == -1) mp[x][y] = t;
		// else mp[x][y] = min(mp[x][y], t);
		for(int i=0; i<4; i++){
			int xi=x+dx[i], yi=y+dy[i];
			if(xi<0 || yi<0) continue; //跳过越界格子
			mp[xi][yi] = (mp[xi][yi]==-1? t : min(mp[xi][yi], t)); //注意取最小
			// if(mp[xi][yi] == -1) mp[xi][yi] = t;
			// else mp[xi][yi] = min(mp[xi][yi], t);
		}
	}
	bfs();
}

O - K Best

3111 -- K Best

二分+贪心,思路不好想到,参考了:K Best(最大化平均值(二分搜索))_Crazy-CSDN博客_kbest

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5+5;
const double eps = 1e-6;
struct node{int v,w,id;} a[maxn];
int n, k; double l, r, s;

bool cmp(const node& a, const node& b) {return a.v-s*a.w > b.v-s*b.w;}
bool check(double s){ //检查平均价值能否到达s
    double sum = 0;
    sort(a+1,a+n+1,cmp);
    for(int i=1; i<=k; i++) sum += a[i].v-s*a[i].w;
    return sum >= 0;
}
int main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin>>n>>k;
    for(int i=1; i<=n; i++) {cin>>a[i].v>>a[i].w; a[i].id=i; r=max(r,(double)a[i].v/a[i].w);}
    while(r-l>eps){
        s = (l+r)/2;
        if(check(s)) l=s;
        else r=s;
    }
    for(int i=1; i<=k; i++) cout<<a[i].id<<' ';
}

 P - Median

3579 -- Median​​​​​​​

两重二分搜索,详见代码。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e6+5;
int n, m, a[maxn];

bool check(int d){
    int cnt = 0;
    for(int i=1; i<n; i++){
        //计算差异值 <= d的数量,是upper_bound找到的位置和当前位置中间夹着的部分
        cnt += upper_bound(a+i+1,a+n+1,a[i]+d)-(a+i)-1;
    }
    return cnt >= m; //数量足够,也可能过多
}
int main(){
    while(scanf("%d",&n) != EOF){
        m = (n*(n-1)/2+1)/2; //差异总数一半向上取整
        for(int i=1; i<=n; i++) scanf("%d",a+i);
        sort(a+1, a+n+1);
        int l=0, r=a[n]-a[1], ans;
        while(l<=r){
            int mid = (l+r)>>1;
            if(check(mid)) {ans=mid; r=mid-1;}
            else l=mid+1;
        }
        printf("%d\n",ans);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值