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;
}