牛客練習賽59 (A、B、C、D)

題目鏈接

A - 小喬和小灰灰

題意:求給定字符串中是否出現“XiaoQiao”和“XiaoHuiHui”

思路:兩次遍歷,將“XiaoQiao”和“XiaoHuiHui”定義爲兩個模式串,在給定字符串中找到相應字符繼續往後推,看最後模式串的下標大小就知道是否爲子序列了。

#include <bits/stdc++.h>
using namespace std;

string s;

int main() {
	cin >> s;
	int n = s.length();
	string t1 = "XiaoQiao";
	string t2 = "XiaoHuiHui";
	bool ok1 = 0, ok2 = 0;
	
	int j = 0; int len1 = t1.length();
	for(int i = 0;i < n; i++) {
		if(t1[j] == s[i]) j++;
	}
	if(j == len1) ok1 = 1;
	
	j = 0; int len2 = t2.length();
	for(int i = 0;i < n; i++) {
		if(t2[j] == s[i]) j++;
	}
	if(j == len2) ok2 = 1;
	
	if(ok1 && ok2) puts("Happy");
	else puts("emm");
}

B - 牛能和小鎮

題意:給n個座標,根據題給的距離費用公式求出需要連接所有點的最小花費。 公式:| xi 2yi - xj 2yj + yi 2(yi - 2xi) - yj 2(yj - 2xj) |

思路:公式可以變形一下就比較直觀:

|( xi 2yi + yi 2(yi - 2xi) )- ( xj 2yj + yj 2(yj - 2xj) ) |

這樣就可以看出來是兩個點各自的座標經過運算後作差。需要最小的差值,那麼排序之後再逐個作差就好了。

#include <bits/stdc++.h>
using namespace std;

long long x[100005], y[100005], len[100005];
int n;

int main() {
	scanf("%d", &n);
	for(int i = 1;i <= n; i++) {
		scanf("%lld %lld", &x[i], &y[i]);
		len[i] = (x[i]*x[i]*y[i]) + (y[i]*y[i]*(y[i]-2*x[i]));
	}
	sort(len+1, len+1+n);
	long long ans = 0;
	for(int i = 1;i < n; i++) {
		ans += len[i+1]-len[i];
	}
	
	printf("%lld\n", ans);
}

C - 裝備合成

題意:一個裝備需要a、b兩種材料合成,2個a 3個b 或者 4個a 1個b 都可以,求給定a和b的個數時最多可以合成多少裝備。

思路:參考這個大佬,貪心做法。

如果 x : y >= 4 : 1,那麼全部使用第二種方案

如果 x : y <= 2 : 3,那麼全部使用第一種方案

介於之間的就優先使用 2 : 3 的方案直到 x : y = 4 : 1,那麼就需要計算需要多少次 2:3的操作就有:

x2ny3n=4\frac{x-2n}{y-3n} = 4

變形一下:

n=4yx10n = \frac{4y-x}{10}

n的值或許會取小數,又可能是第一種方法多一個,也有可能第二種方法多一個,此時四捨五入一下就可以得到:

n=4yx+510n = \frac{4y-x+5}{10}

#include <bits/stdc++.h>
using namespace std;

int _;
long long x, y;

int main() {
	scanf("%d", &_);
	while(_--) {
		scanf("%lld %lld", &x, &y);
		if(y*4 < x) {
			printf("%lld\n", y);
		}
		else if((double)x/(double)y <= (double)2/(double)3) {
			printf("%lld\n", x/2);
		}
		else {
			long long n = (4*y-x+5)/10;
//			cout << "n = " << n << endl;
			x -= 2*n;
			y -= 3*n;
			printf("%lld\n", n + min(x/4, y));
		}
	}
}

D - 取石子游戲

題意:XiaoQiao和XiaoHuiHui兩個人從石堆裏面拿石子,每次把石子分成兩部分,一個從中拿走一部分,另一個繼續把剩下的分爲兩部分,再拿走其中的一部分。石堆如果是偶數的話就平均分,不爲偶數就有一堆可以多出一個。當其中一個人準備拿石子時只剩下一個,那麼這個人就輸了,求兩個人中誰會贏。(XiaoHuiHui先手)

思路:博弈的話先找找規律:

1個石子的時候肯定是後手贏;

2個石子的時候,先手拿走1個,剩下1個,所以先手就贏了;

3個石子分爲 1 和 2, 先手拿走2個,剩下一個,先手贏;

4個石子分爲 2 和 2,先手拿走兩個,此時,可以看成先手變爲後手,2個石子的情形,顯然是2個石子的先手贏,也就是本局的後手;

5個石子分爲 2 和 3,先手拿走哪一個之後剩下的都是對照前面的 2 和 3,得出本劇後手贏

。。。。。。

就這樣一直推可以得出一個結論,石堆一分爲二之後,兩堆石子對應之前有後手贏的時候,本局先手纔可以贏,否則後手贏。

看數據範圍之後這樣一直遞推肯定超時,那麼就想如果可以知道給定的n屬於先手贏還是後手贏的範圍,直接查詢的話就會快很多,在常數級就可以得出答案。

看前面幾個樣例,2和3 是先手贏,4、5、6是後手贏的範圍,4 = 2+2,6 = 3+3, 5 = 2+3;

再看後面的: 7 - 13是先手的範圍:7 = 3+4,9 = 4+5, 11 = 5+6, 13 = 6+7; 8 = 4+4, 10 = 5+5, 12 = 6+6;規律應該就很顯然了,從已知的範圍可以求出後面的範圍,比如4、5、6三個數逐個相加的個數爲 6-4=2; 各自相加的個數爲 6-4+1=3;那麼後面的範圍就是 2+3=5,這樣就可以預處理 1e18之內的先後手贏的範圍,之後輸入時查詢就可以了。

#include <bits/stdc++.h>
using namespace std;
const long long N = 1e18;
vector <pair<long long, long long> > vc;
int _;
long long n;

int main() {
	vc.push_back(make_pair(1, 1));
	vc.push_back(make_pair(2, 3));
	long long tail = 3;
	while(tail < N) {
//		printf("%lld %lld\n", tail+1, tail%2?2*tail:2*tail+1);
		vc.push_back(make_pair(tail+1, tail%2?2*tail:2*tail+1));
		tail = tail%2?2*tail:2*tail+1;
	}
	
	scanf("%d", &_);
	while(_--) {
		scanf("%lld", &n);
		for(int i = 0;i < vc.size(); i++) {
			if(n >= vc[i].first && n <= vc[i].second) {
				if(i%2==0) puts("XiaoQiao");
				else puts("XiaoHuiHui");
				break;
			}
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章