1. 程式人生 > 實用技巧 >Codeforces Round #658 (Div. 2)

Codeforces Round #658 (Div. 2)

A.Common Subsequence

題意

給你兩組數,問你有沒有相同 的書,有的話,輸出最短的那組(大家都知道,1是最小的)

AC

#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
int a[N], x, n, m, flag, t;
int main() {
	//freopen("in.txt","r",stdin);
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> t; while (t--) {
		cin >> n >> m;
		for (int i = 0; i < n; ++i)cin >> x, a[x]++;
		flag = 0;
		for (int i = 0; i < m; ++i) {
			cin >> x;
			if (a[x])
				flag = x;
		}
		if (flag) {
			cout << "YES" << endl;
			cout << 1 << " " << flag << endl;
		}
		else cout << "NO" << endl;
	}
}

B.Sequential Nim

題意:

兩個人玩區石子游戲,有n堆,第i堆有a[i]個,每個人只能按堆的順序拿,就是前面這堆沒有拿完,不能拿下一堆。誰先不能拿就輸了。

思路:

誰先遇到大於1的石子堆,誰就一定不會輸,因為大於1的石子堆,我可以選擇全拿完和留一個,這兩種狀態結果是互斥的,必定會有一個狀態的必勝態,所以只要判斷到大於1的數前面的1的數量就行,若是全1的情況,那就輪流拿,單獨判斷一下就行。

#include<bits/stdc++.h>
using namespace std;
int main() {
    int n, t, x, flag, cnt;
    //freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> t; while (t--) {
        cin >> n; cnt = 0; flag = 0;
        while (n--) {
            cin >> x;
            if (x == 1 && flag == 0)
                cnt++;
            if (x > 1)
                flag = 1;
        }
        if ((cnt % 2 == 1 && flag) || (cnt % 2 == 0 && flag == 0))
            cout << "Second" << endl;
        else
            cout << "First" << endl;
    }
}

C1Prefix Flip (Easy Version)

題意;

給你兩個長度相同的01字串a,b,有一種操作,我們可以把一個字串長度為x的字首拿出來,把0,1互換,(就像是異或一下)然後再把這個字首翻轉(掉個頭)放回到原字串中,問我們通過幾次這種操作把a轉換為b。運算元小於2*n;

思路:

因為我們每次拿的都是字首,那麼也就是說,後面的不會動了,那麼我們可以從後往前來,遇到不同的,就判斷a開頭位置和b當前位置(因為不同就要進行”異或“然後翻轉,會把第一個數翻轉到當前位置上,所以判斷a第一個位置和b當前位置)
如果第一位置和當前位置相同,要把第一位置單獨轉一下,(因為相同,異或再轉過來就不同了)記錄一下每次翻轉的位置就是答案。

#include<bits/stdc++.h>
using namespace std;
int main() {
	//freopen("in.txt","r",stdin);
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	string a, b; int t; cin >> t;
	while (t--) {
		int n; cin >> n;
		cin >> a >> b; int ans = 0;	
		for (int i = 0; i < n; ++i)if (a[i] != b[i])ans++;
		cout << 3 * ans << " ";
		for (int i = 0; i < n; ++i)if (a[i] != b[i])cout << i + 1 << " 1 " << i + 1 << " ";
		cout << endl;
	}
}

C2題待補

D. Unmerge(01揹包問題,1800)

題意:

對於兩個陣列,定義merge運算:每次把兩個陣列的首部的較小的那個拿出,並放到一個新的陣列中。現在給定一個長度為n*2的排列,問你這個排列是否可能由兩個長度為n的陣列merge得到?可能則列印yes,否則no。

思路:

因為每次拿頭部最小的放進新陣列,所以一旦我們碰到一個數值a[i],緊跟a[i]後面的並且小於a[i]的全都應該和a[i]是同一組的,那麼我們對於每次出現的這種,我們記錄它們的長度。如果可能由兩個長度為n的組成,那麼我們可以找到一些長度,他們加起來等於n,也就是01揹包問題了。

#include<bits/stdc++.h>
using namespace std;
const int N = 4e3 + 10;
int dp[N], d[N], a[N];
int main() {
	//freopen("in.txt","r",stdin);
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int t, n; cin >> t; while (t--) {
		cin >> n;
		memset(dp, 0, sizeof dp);
		for (int i = 1; i <= 2 * n; ++i)cin >> a[i];
		int mx = a[1], cnt = 1, len = 0;
		for (int i = 2; i <= 2 * n; ++i) {
			if (a[i] > mx) {
				mx = a[i];
				d[++len] = cnt;
				cnt = 1;
			}
			else
				cnt++;
		}
		d[++len] = cnt;
		for (int i = 1; i <= len; i++)
			for (int j = n; j >= d[i]; j--)
				dp[j] = max(dp[j], dp[j - d[i]] + d[i]);
		if (dp[n] == n) cout << "YES" << endl;
		else cout << "NO" << endl;
	}
}

E題待補