1. 程式人生 > 實用技巧 >萬字長文帶你掌握Java陣列與排序,程式碼實現原理都幫你搞明白!

萬字長文帶你掌握Java陣列與排序,程式碼實現原理都幫你搞明白!

A - Bad Triangle
最小的兩邊和小於等於最大的邊,那麼就一定不會存在這個三角形。否則,一定存在。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<map>
//#include<regex>
#include<cstdio>
#include <iomanip>
#pragma GCC optimize(2)
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
	char ch = getchar(); ll x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 5e4 + 10;
int T, n;
int a[N];
int main() {
	T = read();
	while (T--) {
		n = read();
		upd(i, 1, n)a[i] = read();
		if (a[1] + a[2] <= a[n]) {
			printf("%d %d %d\n", 1, 2, n);
		}
		else {
			puts("-1");
		}
	}
	return 0;
}

B - Substring Removal Game
看著是博弈論,實際就是統計全1段的長度和個數。然後排序後貪心的從最大的開始取即可。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<map>
//#include<regex>
#include<cstdio>
#include <iomanip>
#pragma GCC optimize(2)
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
	char ch = getchar(); ll x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 1e2 + 10;
int T;
char s[N];
int cnt[N];
int main() {
	T = read();
	while (T--) {
		scanf("%s", s+1);
		int len = strlen(s + 1);
		upd(i, 0, len)cnt[i] = 0;
		stack<int >st;
		vector<int>vec;
		upd(i, 1, len) {
			if (st.empty()) {
				st.push(s[i]);
				cnt[1] = 1;
			}
			else {
				if (st.top() != s[i]) {
					st.push(s[i]);
					cnt[st.size()]++;
				}
				else cnt[st.size()]++;
			}
		}
		while (!st.empty()) {
			if (st.top() == '1')vec.push_back(cnt[st.size()]);
			st.pop();
		}
		int ans = 0;
		sort(vec.begin(), vec.end());
		int num = 1;
		for (int i = vec.size()-1; i>=0; i--,num++) {
			if (num & 1)ans += vec[i];
		}
		cout << ans << endl;
	}
	return 0;
}

C - Good Subarrays
連續子段的和是該子段的長度,我們就可以想到,把所有數字全部剪掉1,那麼等價於子段和是零。
問題轉化成求字首和後,找有多少個字首相等的即可。
注意單獨計算字首和是0的字首。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<map>
//#include<regex>
#include<cstdio>
#include <iomanip>
#pragma GCC optimize(2)
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
	char ch = getchar(); ll x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 1e5 + 10;
int T, n;
char a[N];
int aa[N];
ll sum[N];
map<int, ll>mp;
int main() {
	T = read();
	while (T--) {
		n = read();
		upd(i, 0, n)aa[i] = sum[i] = 0;
		mp.clear();
		scanf("%s", a + 1);
		upd(i, 1, n)
			aa[i] = a[i] - 1 - '0';
		ll ans = 0;
		upd(i, 1, n){
			sum[i] = sum[i - 1] + aa[i];
		}
		upd(i, 1, n) {
			if (sum[i] == 0)ans++;
			mp[sum[i]]++;
			ans += mp[sum[i]] - 1;
		}
		printf("%lld\n", ans);
	}
	return 0;
}

D - Colored Rectangles
可以看見的是,我們肯定會想到優先將數字更大的進行匹配,所以我們先進行排序,分別對三種顏色。那麼現在就是,從大到小的取,該怎麼取。
我們利用一個簡單的\(dp\)即可。
\(dp[i][j][k]\)表示三種顏色分別取\(i,j,k\)個的時候的最大值。
就很容易想到,進行簡單的轉移即可。
\(dp[i + 1][j][k + 1] = max(dp[i][j][k] + rr[i] * bb[k], dp[i + 1][j][k + 1]); dp[i][j + 1][k + 1] = max(dp[i][j][k] + gg[j] * bb[k], dp[i][j + 1][k + 1]); dp[i + 1][j + 1][k] = max(dp[i][j][k] + rr[i] * gg[j], dp[i + 1][j + 1][k]);\)

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<map>
//#include<regex>
#include<cstdio>
#include <iomanip>
#pragma GCC optimize(2)
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
	char ch = getchar(); ll x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 210;
int R, G, B;
int r[N], g[N], b[N];
int dp[N][N][N];
vector<int>rr, gg, bb;
bool cmp(int a, int b) {
	return a > b;
}
int ans = 0;
int main() {
	R = read(), G = read(), B = read();
	upd(i, 1, R)r[i] = read(), rr.push_back(r[i]);
	upd(i, 1, G)g[i] = read(), gg.push_back(g[i]);
	upd(i, 1, B)b[i] = read(), bb.push_back(b[i]);
	rr.push_back(0); gg.push_back(0), bb.push_back(0);
	sort(rr.begin(), rr.end(), cmp); sort(gg.begin(), gg.end(), cmp); sort(bb.begin(), bb.end(), cmp);
	upd(i, 0, R) {
		upd(j, 0, G) {
			upd(k, 0, B) {
				dp[i + 1][j][k + 1] = max(dp[i][j][k] + rr[i] * bb[k], dp[i + 1][j][k + 1]);
				dp[i][j + 1][k + 1] = max(dp[i][j][k] + gg[j] * bb[k], dp[i][j + 1][k + 1]);
				dp[i + 1][j + 1][k] = max(dp[i][j][k] + rr[i] * gg[j], dp[i + 1][j + 1][k]);
				ans = max(dp[i + 1][j][k + 1], ans);
				ans = max(dp[i][j + 1][k + 1], ans);
				ans = max(dp[i + 1][j + 1][k], ans);
			}
		}
	}
	cout << ans << endl;
}

E - Two Types of Spells
問題的本質就是,找k個數字,乘以兩倍,其他的數字乘以一倍。(k是閃電的個數)
我們利用兩個集合進行操作。一個集合放兩倍數,一個集合放置一倍數。
當插入的時候,我們優先判斷是否能進行兩倍,即當前數字大於一倍數集合的最大值的時候。
刪除的時候直接進行集合之間的刪除。
除此之外,我們繼續維護一個k,表示當前閃電的個數,即集合-兩倍數的大小。
所以我們每一次再統計答案的時候,先判斷集合-兩倍數是否是大小是k,如果不是,新增或者刪除,從集合-一倍數中。
另外需要排除一種特殊情況,即集合-兩倍數中,全是閃電,那麼這個時候集合兩倍數就需要將最小的閃電,和最大的火進行互換(如果可以的話,否則相當於從兩倍數-集合中刪除該數字)。
可以方便進行,我們直接維護所有數字的和-ans,然後再ans+=sum[兩倍數集合]

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<map>
//#include<regex>
#include<cstdio>
#include <iomanip>
#pragma GCC optimize(2)
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
	char ch = getchar(); ll x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 2e5 + 10;
int n;
set<int >s1, s2, F;
int main() {
	n = read();
	int cnt_l = 0;
	int tp, d;
	ll ans = 0;
	upd(i, 1, n) {
		tp = read(), d = read();
		ans += d;
		if (d > 0) {
			if (tp == 0) {
				F.insert(d);
				if (s2.size() && *s2.begin() <= d)s2.insert(d), ans += d;
				else s1.insert(d);
			}
			else {
				cnt_l++;
				if (s2.size() && *s2.begin() <= d)s2.insert(d), ans += d;
				else s1.insert(d);
			}
		}
		else {
			if (tp == 0) {
				F.erase(-d);
				if (s1.count(-d))s1.erase(-d);
				else s2.erase(-d),ans += d;
			}
			else {
				cnt_l--;
				if (s1.count(-d))s1.erase(-d);
				else s2.erase(-d), ans += d;
			}
		}
		while (s2.size() > cnt_l) {
			ans -= *s2.begin();
			s1.insert(*s2.begin());
			s2.erase(s2.begin());
		}
		while (s2.size() < cnt_l) {
			ans += *s1.rbegin();
			s2.insert(*s1.rbegin());
			s1.erase(--s1.end());
		}
		int x = 0;
		if (s2.size()) {
			if (F.size())x = min(x, *F.rbegin() - *s2.begin());
			else x = -*s2.begin();
		}
		printf("%lld\n", ans + x);
	}
	return 0;
}

F - Controversial Rounds
題目的本質就是,給出一個長度len,判斷最多有少個,不重疊的,長度是len的全\(0\),或者全\(1\)字串。
我們進一步思考可以發現一個貪心的策略。當0,1串重疊的時候,當且僅當出現'?'的時候,我們如果將‘?’歸入當前前面的數字中,情況不會變差,所以基於該貪心,我們可以:
維護一個字尾長度,\(nxt[0][pos],nxt[1][pos]\),以\(pos\)結尾的,全\(0\),全\(1\)串的長度。繼續維護兩個vector,\(vec[0][len],vec[1][len]\)儲存,全\(0\),全\(1\)的長度是\(len\)的所有左端點。
繼而我們可以利用調和級數\(n+n/2+n/3+n/4+...+1=n*\log(n)\)進行暴力判斷。其中還有一個二分的\(log\),當每次找不到的時候,我們二分一個最小的左端點\(>=nowpos\)即可,沒有的話,直接退出。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<map>
//#include<regex>
#include<cstdio>
#include <iomanip>
#pragma GCC optimize(2)
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
	char ch = getchar(); ll x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 1e6 + 1l;
char s[N];
int nxt[2][N];
vector<int> len[2][N];
int n;
bool check(int pos,int ter) {
	if (s[pos] != '?'&&s[pos] != (ter + '0'))return 0;
	else return 1;
}
int main() {
	n = read();
	scanf("%s", s + 1);
	dwd(i, n, 1) {
		if (s[i] == '1' || s[i] == '?')nxt[1][i] = 1 + nxt[1][i + 1];
		if (s[i] == '0' || s[i] == '?')nxt[0][i] = 1 + nxt[0][i + 1];
	}
	upd(bit, 0, 1) {
		for (int l = 1; l <= n;) {
			if (!check(l,bit))l++;
			else {
				int r = l;
				for (r = l; r <= n;) {
					if (check(r, bit))
					{
						len[bit][r - l + 1].push_back(l);
						r++;
					}
					else break;
				}
				l = r + 1;
			}
		}
	}
	upd(x, 1, n) {
		int pos = 1;
		int ans = 0;
		while (pos <= n) {
			if (nxt[0][pos] >= x || nxt[1][pos] >= x)pos += x, ans++;
			else {
				int temp1 = lower_bound(len[0][x].begin(), len[0][x].end(), pos) - len[0][x].begin();
				int temp2 = lower_bound(len[1][x].begin(), len[1][x].end(), pos) - len[1][x].begin();
				int pos1 = INF, pos2 = INF;
				if (temp1 != len[0][x].size())pos1 = len[0][x][temp1];
				if (temp2 != len[1][x].size())pos2 = len[1][x][temp2];
				if (pos1 == INF && pos2 == INF)break;
				pos = min(pos1, pos2);
			}
		}
		printf("%d ", ans);
	}
	return 0;
}