2021暑期牛客训练1A,B,D,F,G题解

A. Alice and Bob
博弈论题目,但是不会博弈论,也不会sg,暴力方法做。
题目描述:有两堆石头,现在A和B要比赛。他们对某个石头可以做这样的操作,在一堆里拿x个,在另一堆拿s*x个(s>=0)。
当某个人不能拿的时候就输了。而Alice是先手。
思路:我们先看(0,0)这种情况,这个时候A会输掉,也就是说(0,0)是后手胜。当我们有一个点能一次性到达(0,0)时,它就是先手胜的。然后我们就把所有(0,0)能到达的点存起来。然后又找到(0,0)不能到达的第一个点,这个时候这个点一定是后手胜的,又用这个点更新他能到达的先手胜的点。
误区:自己做的时候一直把目光放在(2,3)上,想找规律发现找不到,没有想到(0,0)。
代码:

在这里插入代码片#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define ctx cout << "xxxxx" << endl
#define inf 0x3f3f3f3f
#define int long long
#define eps 0.000000001
#define forn for(int i = 1; i <= n; i++)
#define ct1 cout << -1 << endl;
const int INF = 0x3f3f3f3f;
const double pai = 3.141592653589;
const int maxn = 5e3 + 10;
bool book[maxn][maxn];
 

bool cmp(int a, int b) {
    return a < b;
}
 
string s1 = "Alice", s2 = "Bob";
 
signed main() {
    ios::sync_with_stdio(false);
    cout.tie(0);
     
    int tmp = 5000;
 
 
     
    for (int i = 0; i <= tmp; i++) {
        for (int j = 0; j <= tmp; j++) {
                 
            if (!book[i][j]) {
                for (int x = 1; i + x <= tmp; x++) {
                    for (int k = 0; j + k * x <= tmp; k++) {
                         book[i + x][j + k * x] = 1;
                    }
 
                }
                     
                         
                for (int x = 1; j + x <= tmp; x++) {
                    for (int k = 0; i + k * x <= tmp; k++) {
 
                        book[i + k * x][j + x] = 1;
                    }
 
                }
                     
                         
            }
        }
    }  
        int t;
        cin >> t;
        while(t--){
            int n, m;
            cin >> n >> m;
            if(book[n][m]){
                cout << s1 << endl;
            }else{
                cout << s2 << endl;
            }
        }
 
}

B. Ball Dropping
题目图片
高中几何,简单观察,推理。
题目描述:
给你一个a, h, b, r。求球的高度,如果放不下就输出Drop, 否则输出Stuck+高度。
思路:两条辅助线和两组相似三角。(懒得写)。

辅助线

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define ctx cout << "xxxxx" << endl
#define inf 0x3f3f3f3f
#define int long long
#define eps 0.000000001
#define forn for(int i = 1; i <= n; i++)
#define ct1 cout << -1 << endl;
const int INF = 0x3f3f3f3f;
const double pai = 3.141592653589;
const int maxn = 1e3 + 10;

signed main(){
	double r, a, b, h;
	cin >> r >> a >> b >> h;
	double x = r*sqrt(h*h + (a-b)*(a-b)/4 )/h;
	double ans = h*(2*x - b) / (a-b);
	if(2*r >= b){
		cout << "Stuck\n";
		cout << fixed << setprecision(12) << ans << endl;
	}else{
		cout << "Drop\n";
	}
	
}

D. Determine the Photo Position
签到题。
题目描述:
把长为m的连续的222…字符串放到一个二维01字符串数组里,问有几种做法。
思路:找连续的0,大于就记下来,ans = len-m+1。
代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define ctx cout << "xxxxx" << endl
#define inf 0x3f3f3f3f
#define int long long
#define eps 0.000000001
#define forn for(int i = 1; i <= n; i++)
#define ct1 cout << -1 << endl;
const int INF = 0x3f3f3f3f;
const double pai = 3.141592653589;
const int maxn = 2010;
int b[maxn][maxn];
int d[maxn];

int get(int x, int n){
	int ans = 0;
	for(int i = n; i >= n-x+1; i--){
		ans *= i;
	}
	
	return ans;
}

signed main(){
	int n, m;
	cin >> n >> m;
	vector<int> zero;
	for(int i = 1; i <= n;i++){
		string s;
		cin >> s;
		int cot = 0;
	    for(int i = 0; i < s.size(); i++){
	        if(s[i] == '0'){
	        	cot++;
			}else{
				if(cot >= m) zero.push_back(cot);
				cot = 0;
			}
		}
		if(cot >= m) zero.push_back(cot);
	} 	
	string l;
	cin >> l;
	int len = zero.size();
	int ans = 0;
//	cout << "len = " << len << endl;
//	for(int x : zero) cout << x << " ";
//	cout << endl; 
	if(len){
	    for(int i = 0; i < len; i++){	
	    	ans += zero[i] - m + 1;
		}
	}
	cout << ans << endl;
} 

F. Find 3-friendly Integers
暴力找规律问题。
题目描述:首先定义一个3友好型数,就是在这个数中,如果能有模3等于0的数,就是3友好型数。例如102,0被3模就是0,421中,21能被3模为0。现在任意给一个左右区间l,r。求这个闭区间中的满足条件的数。
思路:100以内有76个。在100后都是三位数的数。我们把三位数上的每一位数都进行模三,一共有9*3种情况,去除有0的还剩4种,去除相加起来模三等于0的还剩6种,它们是(都是模数):112, 122, 121, 211, 212, 221。很明显看出来,在这几种情况里要么有12,要么有21,而由21,12组成的数都是符合题目要求的数,因此三位数的数全是3-友好型的数。而4位,5位,等等都是由三位数的数组成的。自然都是。
所以结论就是我们对100以内暴力找。大于100的减去100。
代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define ctx cout << "xxxxx" << endl
#define inf 0x3f3f3f3f
#define int long long
#define eps 0.000000001
#define forn for(int i = 1; i <= n; i++)
#define ct1 cout << -1 << endl;
const int INF = 0x3f3f3f3f;
const double pai = 3.141592653589;
const int maxn = 2010;
signed main() {
	int t;
	cin >> t;
	while(t--){
	    int l , r;
	    cin >> l >> r;
	    int ans = 0;
	    if(l <= 100){
	    	int x = 100;
	    	if(r <= 100){
	    		x = r;
			}
			for(int i = l; i <= x; i++){
				if(i%3 == 0 || (i%10)%3==0 || (i >= 10 && (i/10)%3 == 0)){
					ans++;
				}
			}
			if(x == 100) ans += r-100;
		}else{
			ans = r-l+1;
		}
		cout << ans << endl;
	}
}

G. Game of Swapping Numbers
不好做的题。
题目描述:
两个长为n的数组A,B,现在对A中数组操作k次,然后使得从1到n|A - B|的值求和最大。
思路:首先我们要先看到最大的时候是怎样的。
很明显可以看出一边从小到大,一边从大到小。但是仅仅这样是不足以得到结论的。我们看一个例子,
A: 5 6 7
B: 1 2 3
在这个例子中我们发现无论怎么排,他的结果都是一样的。可以发现的是在|A-B|中,当我们把绝对值拆开,发现的是把所有大的加上,所有小的减去,这样的想法在例子中成立,在任意给的数组A,B中也是成立的。所以我们就可以把问题转换为符号匹配问题。
这个问题中我们把大的赋予’+‘号,小的赋予’-'号。A,B中当 ‘+’ 对应 ‘-’ 时很显然是不用交换的 。所以我们就要尽可能考虑的是(+,+)和(-,-)的交换问题。由于我们只能交换k次,那就要求最大收益。这里的证明可以对(+,+)和(-,-)进行大小假设处理拆开绝对值符号。发现收益是2min(+,+) - 2max(-,-)。
用这个结论就可以把代码写出,先把未交换前的收益选出,然后大于的放一边从小到大排,小于的放一边从大到小排。后就循环k次找收益最大(起码>=0)。当然对于长度为2的要特殊判断。
代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define ctx cout << "xxxxx" << endl
#define inf 0x3f3f3f3f
#define int long long
#define eps 0.000000001
#define forn for(int i = 1; i <= n; i++)
#define ct1 cout << -1 << endl;
const int INF = 0x3f3f3f3f;
const double pai = 3.141592653589;
const int maxn = 5e5 + 10;	
const int N = 1e8 + 10;
int a[maxn], b[maxn];

int _mx[N], _mn[N];


signed main(){
	int n, k;
	cin >> n >> k;
	forn cin >> a[i];
	forn cin >> b[i];
	int ans = 0;
	if(n == 2 && k % 2 != 0){
		swap(a[1], a[2]);
		ans = abs(a[1]-b[1]) + abs(a[2]-b[2]);
		cout << ans << endl;
		return 0;
	}
	forn{
		ans += abs(a[i] - b[i]);
	    _mx[i] = a[i] > b[i] ? a[i] : b[i];
	    _mn[i] = a[i] > b[i] ? b[i] : a[i];
	}
	sort(_mx+1, _mx+1+n);
	sort(_mn+1, _mn+1+n,greater<int>());
	int i = 1;
    for(int i = 1; i <= k && i <= n; i++){
        ans += max(2*(_mn[i]-_mx[i]), (int)0);
    }
	cout << ans << endl;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值