2023秋季地大北京算法课

pta 传送门
不贴完整代码防止 CV 做题

pta 习题摘记

7 - 1 GPA

void solve(){
	int n;
	cin >> n;
	vector<int> s(n), v(n);
	for(int i = 0; i < n; ++ i) cin >> s[i];
	double sum = 0, vsum = 0;
	for(int i = 0; i < n; ++ i){
		cin >> v[i];
		vsum += v[i];
		if(s[i] >= 90) sum += 4.0 * v[i];
		else if(s[i] >= 85) sum += 3.5 * v[i];
		else if(s[i] >= 80) sum += 3.0 * v[i];
		else if(s[i] >= 75) sum += 2.5 * v[i];
		else if(s[i] >= 70) sum += 2.0 * v[i];
		else if(s[i] >= 65) sum += 1.5 * v[i];
		else if(s[i] >= 60) sum += 1.0 * v[i];
	}
	cout << fixed << setprecision(2) << sum / vsum << '\n';
	return ;
}

7 - 2 小伍的升序数组

void solve(){
	int n;
	cin >> n;
	vector<int> a(n);
	bool f = true;
	for(int i = 0; i < n ; ++ i){
		cin >> a[i];
		if(i && a[i] < a[i - 1]) f = false;
	}
	if(f) cout << "Yes\n";
	else cout << "No\n";
	return ;
}

7 - 3 两数之和

void solve(){
	int n, k;
	cin >> n >> k;
	vector<int> a(n);
	for(int i = 0; i < n; ++ i){
		cin >> a[i];
	}
	map<int, int> q;
	ll ans = 0;
	for(int i = n - 1; i >= 0; -- i){
		if(q.count(k - a[i])){
			ans += q[k - a[i]];
		}
		++ q[a[i]];
	}
	cout << ans << '\n';
	return ;
}
void solve(){
	int n, k;
	cin >> n >> k;
	vector<int> a(n);
	ll ans = 0;
	for(int i = 0; i < n; ++ i){
		cin >> a[i];
		for(int j = 0; j < i; ++ j)
			if(a[i] + a[j] == k) ++ ans;
	}
	cout << ans << '\n';
	return ;
}

7 - 4 归并排序

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int maxm = 2e5 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;
ll n;
vector<ll> r(maxm + 1, 0), r1(maxm, 0);

void Merge(int s, int m, int t){//合并两个升序数组为一个升序数组
	int i = s, j = m + 1, k = s;
	while(i <= m && j <= t){
		if(r[i] <= r[j]) r1[k ++] = r[i ++];
		else r1[k ++] = r[j ++];
	}
	while(i <= m) r1[k ++] = r[i ++];
	while(j <= t) r1[k ++] = r[j ++];
	return ;
}

void Merge_Sort(int s, int t){//归并排序
	if(s != t){//可以划分
		int m = (s + t) / 2;
		Merge_Sort(s, m);//求解子问题1
		Merge_Sort(m + 1, t);//求解子问题2
		Merge(s, m, t);//合并两个序列
		for(int i = s; i <= t; ++ i)//结果复制到r数组中
			r[i] = r1[i];
	}
	return ;
}

void solve(){
	cin >> n;
	for(int i = 1; i <= n; ++ i) cin >> r[i];
	Merge_Sort(1, n);
	for(int i = 1; i <= n; ++ i) cout << r[i] << " \n"[i == n];
	return ;
}

signed main(){
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	// cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}

7 - 5 棋盘覆盖问题

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int maxm = 2e2 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;

int idx = 0, board[maxm][maxm];

// 棋盘左上坐标[tr, tc],棋盘边长为 size,特殊方格位置为 [dr, dc]
void ChessBoard(int tr, int tc, int size, int dr, int dc){
	if(size == 1) return ;// 只有一个特殊方格时
	int id = ++ idx, s = size / 2;
	if(dr < tr + s && dc < tc + s){// 特殊方格在左上子棋盘中
		ChessBoard(tr, tc, s, dr, dc);
	}else{
		board[tr + s - 1][tc + s - 1] = id;
		ChessBoard(tr, tc, s, tr + s - 1, tc + s - 1);
	}

	if(dr < tr + s && dc >= tc + s){// 特殊方格在右上子棋盘中
		ChessBoard(tr, tc + s, s, dr, dc);
	}else{
		board[tr + s - 1][tc + s] = id;
		ChessBoard(tr, tc + s, s, tr + s - 1, tc + s);
	}

	if(dr >= tr + s && dc < tc + s){// 特殊方格在左下子棋盘中
		ChessBoard(tr + s, tc, s, dr, dc);
	}else{
		board[tr + s][tc + s - 1] = id;
		ChessBoard(tr + s, tc, s, tr + s, tc + s - 1);
	}

	if(dr >= tr + s && dc >= tc + s){// 特殊方格在右下子棋盘中
		ChessBoard(tr + s, tc + s, s, dr, dc);
	}else{
		board[tr + s][tc + s] = id;
		ChessBoard(tr + s, tc + s, s, tr + s, tc + s);
	}
	return ;
}

void solve(){
	int n, x, y;
	cin >> n >> x >> y;
	ChessBoard(1, 1, n, x, y);
	for(int i = 1; i <= n; ++ i){
		for(int j = 1; j <= n; ++ j)
			cout << setw(5) << left << board[i][j];
		cout << '\n';
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	// cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}

快速幂

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int maxm = 2e5 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;

// ll qpow(ll a, ll x, ll p){
//     a %= p;
//     ll res = 1;
//     while(x){
//         if(x & 1) res = res * a % p;
//         x >>= 1; a = a * a % p;
//     }
//     return res;
// }

ll qpow(ll x, ll y, ll m) {
	// cout << x << " " << y << " " << m << endl;
	if(y == 0) {
		return 1;
	}
	if(y % 2 == 1) {
		return x * qpow(x, y - 1, m) % m;
	} else {
		ll temp = qpow(x, y / 2, m) % m;
		return temp * temp % m;
	}
}

void solve(){
	ll a, n, m;
	cin >> a >> n >> m;
	cout << qpow(a, n, m) << '\n';
	return ;
}

signed main(){
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	// cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}

7 - 6 最大字段和问题(分治版)

const int maxm = 1e5 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;
ll n, a[maxm];

ll calc(int l, int r){
	if(l == r) return a[l];
	int m = l + r >> 1;
	ll max1 = calc(l, m), max2 = calc(m + 1, r), max3;
	ll maxl = 0, maxr = 0, t;
	t = 0;
	for(int i = m; i >= l; -- i){
		t += a[i];
		maxl = max(maxl, t);
	}
	t = 0;
	for(int i = m + 1; i <= r; ++ i){
		t += a[i];
		maxr = max(maxr, t);
	}
	max3 = maxl + maxr;
	return max(max1, max(max2, max3));
}

void solve(){
	cin >> n;
	for(int i = 1; i <= n; ++ i) cin >> a[i];
	cout << calc(1, n);
	return ;
}

国王的奖励(分治版)

题意
国际象棋是很久以前由一个印度人Shashi发明的,当他把该发明献给国王的时候,国王很高兴,就许诺可以给这个发明人任何他想要的奖赏。Shashi要求以这种方式给他一些粮食:棋盘的第1个方格里只放\(1\)粒麦粒,第2格\(q\)个,第三格\(q^2\)个,第四格\(q^3\)个⋯,直到n个格子全部放满。这个奖赏最终会是什么样子的呢?Shashi已经有算法了,请你算一算吧。

当然Shashi并不关心具体的数有多少了,他有一个检验你的答案是否与他心意相通的办法:把你求出的答案对100000007取模看看和Shashi算的是否一样就行了。

注:\(1 \le n, q \le 10^{18}\)
思路

代码
法一:等比数列求和公式

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int maxm = 2e5 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 100000007;
ll n, q, ans;

ll qpow(ll a, ll x, ll p){
	a %= p;
	ll res = 1;
	while(x){
		if(x & 1) res = res * a % p;
		x >>= 1; a = a * a % p;
	}
	return res;
}

void solve(){
	cin >> q >> n;
	q %= mod;// 精彩的处理!!!
	if(q == 1) ans = n % mod;
	else{
		ans = (mod + qpow(q, n, mod) - 1) % mod * qpow(q - 1, mod - 2, mod) % mod;
	}
	cout << ans << '\n';
	return ;
}

signed main(){
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}

7 - 7 平面最近点对(分治版)

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long
 
using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*
 
*/
const int N = 2e5 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;
ll n;
pdd p[N], t[N];
 
double dis(pdd & i, pdd & j){
	double dx = i.first - j.first, dy = i.second - j.second;
	return sqrt(dx * dx + dy * dy);
}
 
double closedots(int l, int r){
	if(l == r) return 2e9;
	if(r == l + 1) return dis(p[l], p[r]);
	int mid = (l + r) >> 1;
	double d1, d2, d;
	d1 = closedots(l, mid); d2 = closedots(mid + 1, r);
	d = min(d1, d2);
	int id = 0;
	for(int i = l; i <= r; ++ i)
		if(fabs(p[i].first - p[mid].first) < d) t[++ id] = p[i];
	sort(t + 1, t + 1 + id, [](pdd& a, pdd& b){
		return a.second < b.second;
	});
	for(int i = 1; i < id; ++ i)
		for(int j = i + 1; j <= id && t[j].second - t[i].second < d; ++ j)
			d = min(d, dis(t[i], t[j]));
	return d;
}
 
void solve(){
	cin >> n;
	for(int i = 1; i <= n; ++ i) cin >> p[i].first >> p[i].second;
	sort(p + 1, p + 1 + n, [](pdd& a, pdd& b){
		return a.first < b.first || a.first == b.first && a.second < b.second;
	});
	double ans = closedots(1, n);
	cout << fixed << setprecision(4) << ans << '\n';
	return ;
}
 
signed main(){
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	// cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}

7 - 8 寻找第 k 小(分治版)

C++ 函数:nth_element(first, k, last);
或者
利用快速排序的划分思想找第 k 个元素

快速排序的划分会将所有比数 i 小的元素放在其前面,比其大的元素放在后面,那么数 i 在数组中的排名就知道了,那么再比较排名与 k 的值,相等即找到,不等则递归左右序列找第 k 大的元素

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int N = 2e6 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;
int n, k, a[N];

int partition(int begin, int end){// 划分
	int i = begin, j = end;
	while(i < j){
		while(i < j && a[i] <= a[j]) -- j;// 右侧扫描
		if(i < j){// 将小元素放在前面
			int temp = a[i];
			a[i] = a[j];
			a[j] = temp;
			++ i;
		}
		while(i < j && a[i] <= a[j]) ++ i;// 左侧扫描
		if(i < j){// 将大元素放在后面
			int temp = a[i];
			a[i] = a[j];
			a[j] = temp;
			-- j;
		}
	}
	return i;
}

int find_kth(int l, int r, int k){
	int p = partition(l, r);
	if(p == k) return p;
	else if(p < k) return find_kth(p + 1, r, k);
	else return find_kth(l, p, k);
}

void solve(){
	cin >> n >> k;
	for(int i = 1; i <= n; ++ i) cin >> a[i];
	cout << a[find_kth(1, n, k)] << '\n';
	return ;
}

signed main(){
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	// cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}

7 - 9 铺设油井管道plus

题面
某石油公司有 n 口油井,为方便输送石油,计划修建输油管道。根据设计要求,水平方向有一条主管道,每口油井修一条垂直方向的支线管道通向主管道。请设计一种算法确定主管道的位置,使得所有油井到主管道之间的支线管道长度的总和最小。提示:复杂度为 O(n) 能通过所有测试用例。

每个输入文件为一个测试用例,每个文件的第一行给出一个正整数 n \((1\le n \le 2·10^6)\),表示油井数量,从第二行起的 n 行数据,表示每口油井的位置,每行包含以空格分隔的两个整数,分别表示每口油井的横坐标 x \((-10^9 \le x \le 10^9)\)和纵坐标 y \((-10^9 \le y \le 10^9)\)

输出各油井到主管道之间的支管道最小长度总和。

思路
思路就是利用 7 - 8 的代码找中位数即为主管道的 y 坐标,之后遍历求解
快读可过,关流 cin 目前过不了300 ms

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int N = 2e6 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;
int n, k, a[N];

template<typename T>inline void read(T &x) {
	int f = 1;
	x = 0;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
	x *= f;
}

int partition(int begin, int end){// 划分
	int i = begin, j = end;
	while(i < j){
		while(i < j && a[i] <= a[j]) -- j;// 右侧扫描
		if(i < j){// 将小元素放在前面
			int temp = a[i];
			a[i] = a[j];
			a[j] = temp;
			++ i;
		}
		while(i < j && a[i] <= a[j]) ++ i;// 左侧扫描
		if(i < j){// 将大元素放在后面
			int temp = a[i];
			a[i] = a[j];
			a[j] = temp;
			-- j;
		}
	}
	return i;
}

int find_kth(int l, int r, int k){
	int p = partition(l, r);
	if(p == k) return p;
	else if(p < k) return find_kth(p + 1, r, k);
	else return find_kth(l, p, k);
}

void solve(){
	cin >> n;
	for(int i = 1; i <= n; ++ i){
		read(a[i]); read(a[i]);
	}
	int pos = find_kth(1, n, (n + 1) / 2);
	ll ans = 0;
	for(int i = 1; i <= n; ++ i) ans += abs(a[i] - a[pos]);
	printf("%lld\n", ans);
	return ;
}

signed main(){
	// ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	// cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}

7-10 两个序列的中位数(减治法)

此题为求解两个等长升序数组的中位数

算法的基本思想如下:
(1)分别求出两个序列的中位数 a, b
(2)比较 a 和 b,会有如下三种情况:

  • a = b,则 a 即为两个序列的中位数
  • a < b,则中位数出现在 a 和 b 之间,在序列 A 中舍弃前半段,序列 B 中舍弃后半段
  • a > b,则中位数出现在 b 和 a 之间,在序列 B 中舍弃前半段,序列 A 中舍弃后半段

(3)再新序列 A,B 中分别求出中位数,重复上诉操作,直到两个序列中只有一个元素,则较小者即为所求

整体的时间复杂度为 \(O(\log_2n)\)

int search_mid(vector<int> & a, vector<int> & b, int n){
	int l[2] = {1, 1}, r[2] = {n, n}, mid[2];
	while(l[0] < r[0] && l[1] < r[1]){
		mid[0] = l[0] + r[0] >> 1;
		mid[1] = l[1] + r[1] >> 1;
		if(a[mid[0]] == b[mid[1]]) return a[mid[0]];
		if(a[mid[0]] < b[mid[1]]){
			if((r[0] + l[0]) % 2) l[0] = mid[0] + 1;
			else l[0] = mid[0];
			r[1] = mid[1];
		}else{
			if((r[0] + l[0]) % 2) l[1] = mid[1] + 1;
			else l[1] = mid[1];
			r[0] = mid[0];
		}
	}
	if(a[l[0]] < b[l[1]]) return a[l[0]];
	else return b[l[1]];
}

void solve(){
	int n;
	cin >> n;
	vector<int> a(n + 1), b(n + 1);
	for(int i = 1; i <= n; ++ i) cin >> a[i];
	for(int i = 1; i <= n; ++ i) cin >> b[i];
	cout << search_mid(a, b, n) << '\n';
	return ;
}

7-11 淘汰赛冠军(减治法)

假设有 \(n = 2^k\) 个选手进行竞技淘汰赛,最后决出冠军的选手
规则如下:
开始时将所有选手分成 n/2 组,每组两个选手进行比赛,被淘汰者不参加以后的比赛,然后再将剩余选手分成 n/4 组,每组两个选手进行比赛,......直到剩余最后两个选手,进行一次比赛即可选出最后的冠军。下图(减治法求解淘汰赛冠军问题示例图)给出了一个减治技术解决淘汰赛冠军问题的过程示例(假设按照字符编码进行比较)。
image

时间复杂度为 \(O(n)\)

pta 给出的题每一轮选手的能力值需乘权重取余,基本思想还是上面的思想

const int N = (1 << 19) + 5, M = 20 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;
ll n, k, a[N], b[M], r[N];

int comp(int x, int y, int c){
	ll l = a[x] * b[c] % mod, r = a[y] * b[c] % mod;
	if(l < r || l == r && y < x) return true;
	else return false;
}

int Game(){
	int i = n, c = 1;
	while(i > 1){
		i /= 2;
		for(int j = 1; j <= i; ++ j){
			if(comp(r[j], r[j + i], c))
				r[j] = r[j + i];
		}
		++ c;
	}
	return r[1];
}

void solve(){
	cin >> n >> k;
	for(int i = 1; i <= k; ++ i) cin >> b[i];
	for(int i = 1; i <= n; ++ i) cin >> a[i], r[i] = i;
	cout << Game() << '\n';
	return ;
}

7-12 数塔

简单动态规划,洛谷传送门

从下往上

void solve(){
	int n;
	cin >> n;
	vector a(n + 1, vector<int>(n + 1));
	for(int i = 1; i <= n; ++ i){
		for(int j = 1; j <= i; ++ j){
			cin >> a[i][j];
		}
	}
	for(int i = n - 1; i >= 1; -- i){
		for(int j = 1; j <= i; ++ j){
			a[i][j] += max(a[i + 1][j], a[i + 1][j + 1]);
		}
	}
	cout << a[1][1] << '\n';
	return ;
}

从上往下

void solve(){
	int n;
	cin >> n;
	vector a(2, vector<int>(n + 1, 0));
	int p = 0;
	for(int i = 1; i <= n; ++ i){
		for(int j = 1; j <= i; ++ j){
			cin >> a[p][j];
			a[p][j] += max(a[1 - p][j], a[1 - p][j - 1]);
		}
		p = 1 - p;
	}
	cout << *max_element(a[1 - p].begin(), a[1 - p].end()) << '\n';
	return ;
}

7-13 最短路径

多段图最短路径问题,对于每个顶点,考虑其所有入边,从最小的起始距离转移而来

int n, m;
vector<pii> e[N];

void solve(){
	cin >> n >> m;
	for(int i = 0; i < m; ++ i){
		int x, y, z;
		cin >> x >> y >> z;
		e[x].push_back({y, z});
		e[y].push_back({x, z});
	}
	vector<int> dis(n + 1, inf);
	dis[1] = 0;
	for(int i = 1; i <= n; ++ i){
		for(auto & [v, w] : e[i]){
			dis[v] = min(dis[v], dis[i] + w);
		}
	}
	cout << dis[n];
	return ;
}

7-14 旅行商问题

从某个城市出发,经过每个城市仅一次后返回出发城市,问你最小的路程之和
考虑状压DP求解该问题

状态
\(dp[i][j]\) 表示从城市 \(i\) 出发经过集合 \(j\) 中的所有城市并返回城市 \(s\) 的最小路程和

转移
利用二进制 1/0 来表示某个城市经过或者没经过
选择城市 0 作为出发城市(无论哪个城市出发都一样)
\(dp[i][0] = a[i][0]\)
\(dp[i][j] = \min{ \{a[i][k] + dp[k][j - \{ k \} \}}, (k \in j)\)

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int N = 20 + 5, M = (1 << 20), inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;
int n, s, a[N][N], dp[N][M];

void tsp(){
	for(int i = 0; i < n; ++ i){
		dp[i][0] = a[i][0];
	}
	for(int j = 1; j < (1 << n); ++ j){
		for(int i = 0; i < n; ++ i){
			dp[i][j] = inf;
			if(i && ((j >> (i - 1)) & 1) == 1) continue;// 如果后缀集合中包含 i,则跳过
			for(int k = 1; k < n; ++ k){// 枚举剩下的 n - 1 个城市
				if(((j >> (k - 1)) & 1) == 0) continue;
				dp[i][j] = min(dp[i][j], a[i][k] + dp[k][j ^ (1 << (k - 1))]);
			}
		}
	}
	return ;
}

void solve(){
	cin >> n >> s;
	for(int i = 0; i < n; ++ i){
		for(int j = 0; j < n; ++ j){
			cin >> a[i][j];
		}
	}
	tsp();
	cout << dp[0][(1 << (n - 1)) - 1] << '\n';
	return ;
}

signed main(){
	// freopen("in.txt", "r", stdin);
	// freopen("out.txt", "w", stdout);
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	// cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}

7-15 最长有序子序列

朴素的最长上升子序列 \(O(n^2)\) 或者 \(O(n \log_2 n)\) 都可以通过

  • \(O(n^2)\)
void solve(){
	int n;
	cin >> n;
	vector<int> dp(n, 1), a(n);
	for(int i = 0; i < n; ++ i){
		cin >> a[i];
		for(int j = 0; j < i; ++ j)
			if(a[i] > a[j]) dp[i] = max(dp[i], dp[j] + 1);
	}
	cout << (*max_element(dp.begin(), dp.end()));
	return ;
}
  • \(O(n \log_2 n)\)
void solve(){
	int n;
	cin >> n;
	vector<int> dp(n + 5, inf);
	for(int i = 0; i < n; ++ i){
		int t;
		cin >> t;
		auto it = lower_bound(dp.begin() + 1, dp.end(), t) - dp.begin();
		dp[it] = min(dp[it], t);
	}
	cout << lower_bound(dp.begin() + 1, dp.end(), inf) - dp.begin() - 1;
	return ;
}

7-16 最长公共子序列长度

朴素的 \(O(n^2)\) 做法

void solve(){
	string a, b;
	cin >> a >> b;
	a = '#' + a; b = '#' + b;
	vector dp(a.size() + 5, vector<int>(b.size() + 5, 0));
	for(int i = 1; i < a.size(); ++ i){
		for(int j = 1; j < b.size(); ++ j){
			if(a[i] == b[j]) dp[i][j] = dp[i - 1][j - 1] + 1;
			else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
		}
	}
	cout << dp[a.size() - 1][b.size() - 1];
	return ;
}

7-17 0-1背包

朴素的 \(O(NV)\) 做法

int n, c;
vector<int> dp(N, 0);

void solve(){
	cin >> n >> c;
	for(int i = 0; i < n; ++ i){
		int v, w;
		cin >> v >> w;
		for(int j = c; j >= v; -- j)
			dp[j] = max(dp[j], dp[j - v] + w);
	}
	cout << dp[c] << '\n';
	return ;
}

7-18 h0145. 会议安排

贪心将所有活动按照结束时间非降序排序,再从前往后安排活动

void solve(){
	int n, ans = 1;
	cin >> n;
	vector<pii> a(n);
	for(auto & [x, y] : a) cin >> x >> y;
	sort(a.begin(), a.end(), [&](pii x, pii y){
		return x.second < y.second;
	});
	for(int i = 1, j = 0; i < n; ++ i){
		if(a[i].first >= a[j].second){
			++ ans;
			j = i;
		}
	}
	cout << ans << '\n';
	return ;
}

7-19 小伍的背包问题

物品可以均匀切割的背包,贪心取价值重量比最大的一个一个放入背包即可

void solve(){
	int n, c;
	cin >> n >> c;
	vector<pll> a(n);
	for(auto &[w, v] : a) cin >> v >> w;
	sort(a.begin(), a.end(), [&](pll x, pll y){
		return x.first * y.second > y.first * x.second;
	});
	ll ans = 0;
	for(int i = 0; i < n && c; ++ i){
		if(a[i].second <= c){
			ans += a[i].first;
			c -= a[i].second;
		}else{
			ans += 1.0 * a[i].first * c / a[i].second;
			c = 0;
		}
	}
	cout << ans << '\n';
	return ;
}

7-20 Prim算法

Prim 算法板子题,注意 Prim 的写法,不然容易 TLE

ll n;
bool vis[N];
vector<pll> p(N);

ll getdis(int i, int j){
	return abs(p[i].first - p[j].first) + abs(p[i].second - p[j].second);
}

void solve(){
	cin >> n;
	for(int i = 0; i < n; ++ i){
		int x, y;
		cin >> x >> y;
		p[i] = {x / 8, y / 8};
	}
	priority_queue<pll, vector<pll>, greater<pll>> q;
	vector<ll> dis(n + 1, INF);
	q.push({0, 0});
	dis[0] = 0;
	ll ans = 0;
	while(q.size()){
		auto [d, u] = q.top();
		q.pop();
		if(vis[u]) continue;
		vis[u] = true;
		dis[u] = d;
		ans += d;
		for(int i = 0; i < n; ++ i){
			if(!vis[i]){
				ll len = getdis(i, u);
				if(dis[i] > len){
					dis[i] = len;
					q.push({len, i});
				}
			}
		}
	}
	cout << ans << '\n';
	return ;
}

7-21 批处理作业调度

int n, a[N], b[N], sum[N][2];
ll ans = inf;
bool vis[N];

void dfs(int u){
	if(u == n + 1){
		ll t = 0;
		for(int i = 1; i <= n; ++ i)
			t += sum[i][1];
		ans = min(ans, t);
		return ;
	}
	for(int i = 1; i <= n; ++ i){
		if(!vis[i]){
			vis[i] = true;
			sum[u][0] = sum[u - 1][0] + a[i];
			sum[u][1] = max(sum[u][0], sum[u - 1][1]) + b[i];
			dfs(u + 1);
			vis[i] = false;
		}
	}
	return ;
}

void solve(){
	cin >> n;
	for(int i = 1; i <= n; ++ i) cin >> a[i];
	for(int i = 1; i <= n; ++ i) cin >> b[i];
	dfs(1);
	cout << ans << '\n';
	return ;
}

7-22 八皇后问题(*)

注意输出格式。。。

int n;
char ch[N][N];
bool c[N], l[N << 1], r[N << 1], flag;

void dfs(int u){
	if(u == n + 1){
		if(flag) cout << '\n';
		flag = true;
		for(int i = 1; i <= n; ++ i){
			for(int j = 1; j <= n; ++ j){
				cout << ch[i][j] << " \n"[j == n];
			}
		}
		return ;
	}
	for(int i = 1; i <= n; ++ i){
		int x = u + i, y = n + 1 - i + u;
		if(!c[i] && !l[x] && !r[y]){
			c[i] = l[x] = r[y] = true;
			ch[u][i] = 'Q';
			dfs(u + 1);
			c[i] = l[x] = r[y] = false;
			ch[u][i] = '.';
		}
	}
	return ;
}

void solve(){
	cin >> n;
	for(int i = 1; i <= n; ++ i)
		for(int j = 1; j <= n; ++ j)
			ch[i][j] = '.';
	dfs(1);
	if(!flag) cout << "None\n";
	return ;
}

7-23 图着色问题

#include<bits/stdc++.h>

using namespace std;
const int N = 5 + 5;
int n, em, col[N];
vector<int> e[N];

bool check(int u){
	for(auto v : e[u])
		if(col[u] == col[v])
			return false;
	return true;
}

bool GraphColor(int m){
	for(int i = 1; i <= n; ++ i) col[i] = 0;
	int k = 1;
	while(k >= 1){
		++ col[k];
		while(col[k] <= m){
			if(check(k)) break;
			else ++ col[k];
		}
		if(col[k] <= m && k == n) return true;
		if(col[k] <= m && k < n) ++ k;
		else col[k --] = 0;
	}
	return false;
}

signed main(){
	cin >> n >> em;
	for(int i = 0; i < em; ++ i){
		int u, v;
		cin >> u >> v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	for(int i = 1; i <= n; ++ i){
		if(GraphColor(i)){
			cout << i << '\n';
			break;
		}
	}
	return 0;
}

7-24 素数环

bool flag = false, vis[N], isprime[N << 1];
int n, a[N];

void pre(){
	for(int i = 2; i < (N << 1); ++ i){
		isprime[i] = true;
		for(int j = 2; j * j <= i; ++ j){
			if(i % j == 0){
				isprime[i] = false;
				break;
			}
		}
	}
	return ;
}

bool check(int u){
	bool f = true;
	if(u > 1) f = f && isprime[a[u] + a[u - 1]];
	if(u == n) f = f && isprime[a[u] + a[1]];
	return f;
}

void dfs(int u){
	if(u == n + 1){
		flag = true;
		for(int i = 1; i <= n; ++ i)
			cout << a[i] << " \n"[i == n];
		return ;
	}
	for(int i = 1; i <= n; ++ i){
		a[u] = i;
		if(!vis[i] && check(u)){
			vis[i] = true;
			dfs(u + 1);
			vis[i] = false;
			a[u] = 0;
		}
	}
	return ;
}

void solve(){
	pre();
	cin >> n;
	a[1] = 1;
	vis[1] = true;
	dfs(2);
	if(!flag) cout << "No Answer\n";
	return ;
}
posted @ 2023-10-10 02:06  Qiansui  阅读(23)  评论(0)    收藏  举报