chika和蜜柑(贪心/优先队列)and you和帆船(枚举)and 数位染色(状压枚举/dfs)

118 阅读3分钟

chika和蜜柑

在这里插入图片描述

(1)思路:贪心

按照甜度最大的来取(从大到小排序),遇到相同甜度的去酸度最小(从小到大排序),可以用sort也可以用优先队列来写;

(2)测试数据

3 2
1 3 4
2 2 5

(3)代码(贪心思想 + sort)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
int n;
// 在C++的语法里面,可以直接用 结构体名字来定义变量,而无需用typedef重命名 
struct migan {
	ll sweet;
	ll sour;
};
bool cmp(migan a, migan b) {
	if (a.sweet == b.sweet) {
		return a.sour < b.sour;
	} else {
		return a.sweet > b.sweet;
	}
}
int k;
migan nums[200005]; 
ll sumSweet;
ll sumSour;
int main (){
	cin >> n >> k;
	for (int i = 0; i < n; ++i) {
		cin >> nums[i].sour;
	}
	for (int i = 0; i < n; ++i) {
		cin >> nums[i].sweet;
	}
	
	sort(nums, nums + n, cmp);
	
	for (int i = 0; i < k; ++i) {
		sumSweet += nums[i].sweet;
		sumSour += nums[i].sour;
	}
	
	cout << sumSour << ' ' << sumSweet;
	return 0;
} 

you和帆船(枚举)

在这里插入图片描述

(1)测试数据

2
1 0
0 1

(2)代码(思路在注释中)

// 直接枚举 
//2 ≤n ≤2000 可以枚举 
#include <bits/stdc++.h>
using namespace std;
int n;
const int N = 2005;
int a[N][2]; 
double minDis = 1e9; //最小路径和 
int main () {
	double tmp; 
	cin >> n;
	// 输入宝藏的地点 
	for (int i = 0; i < n; ++i) {
		cin >> a[i][0] >> a[i][1]; // 0代表x坐标,1代表y坐标 
	}
	
	// 搜寻两个宝藏,三条边,(0,0)起始点-->第一个宝藏-->第二个宝藏-->回到起始点(0,0) 
	for (int i = 0; i < n - 1; ++i) {
		for (int j = i + 1; j < n; ++j) {
			// 距离公式 
			minDis = min(minDis, sqrt(a[i][0] * a[i][0] + a[i][1] * a[i][1]) +sqrt (a[j][0] * a[j][0] + a[j][1] * a[j][1]) + sqrt((a[i][0] - a[j][0]) * (a[i][0] - a[j][0]) + (a[i][1] - a[j][1]) * (a[i][1] - a[j][1])));
		}
	} 
	cout << fixed << setprecision(6) << minDis;
	return 0;
}

数位染色(状压枚举/dfs)

在这里插入图片描述 在这里插入图片描述

(1)思路:

x的范围不超过1e18,所以x最多只有19位数字。O(n2^n)状压枚举每位选或不选即可通过。(每个位置上两种状态0或1,多少位置,2的多少次方)还需要遍历n; 代码中有注释,可以先看一遍代码,方便理解

(2)测试数据

1234567

(3)代码(状压---状态压缩)

// 状压基本模板 
#include <bits/stdc++.h>
using namespace std;
#define ll long long 
int zt[22]; //代表每一位上的数字,等下要跟状态相结合
int sum, ans;
ll num; 
// ans 是原始的数,每位数字的总和
// sum 是后面染色的数字和
// 我们的判断依据就是,若sum * 2 == ans,说明染色的数字和==未染色数字和 (仔细想想,即染色数字和就是总和的一半) 
int main () {
	int j = 0;
	cin >> num; //输入这个数字  123456
	// 将每一位拆解开
	while (num) {
		zt[j++] = num % 10; // 从最低位开始,一个一个存进zt数组中( 倒着的) 654321
		ans += num % 10; //再将每一位的数字求和 
		num /= 10; // 求下一位数字 
		
	} 
	// 最后得出的j,就知道有多少位了 
	// 枚举状态 
	// 1 << j 即二进制 左移 就是乘,右移即除,代表2^j个状态 
	for (int i = 0; i < 1 << j; ++i) {
		int p = i; // 取出i(十进制) 
		sum = 0;
		//转换成二进制(状态),并且与位置上的数字结合
		// p % 2 就是从最低位开始,取二进制数 010101... 
		// k = 0,也是最低位
		// 求和 1 代表染色,0代表不染色,染色就把和加上 
		for (int k = 0; k < j; ++k) {
			sum += p % 2 * zt[k];
			p /= 2; // 求下一位 
		} 
		if (sum * 2 == ans) {
			cout << "Yes";
			return 0;
		} 
	} 
	cout << "No";
	
	return 0;
}
  • 对二进制处理的时候,&1,>> 1,位运算比 % /更快
// 状压基本模板 
#include <bits/stdc++.h>
using namespace std;
#define ll long long 
int zt[22]; //代表每一位上的数字,等下要跟状态相结合
int sum, ans;
ll num; 
// ans 是原始的数,每位数字的总和
// sum 是后面染色的数字和
// 我们的判断依据就是,若sum * 2 == ans,说明染色的数字和==未染色数字和 (仔细想想,即染色数字和就是总和的一半) 
int main () {
	int j = 0;
	cin >> num; //输入这个数字  123456
	// 将每一位拆解开
	while (num) {
		zt[j++] = num % 10; // 从最低位开始,一个一个存进zt数组中( 倒着的) 654321
		ans += num % 10; //再将每一位的数字求和 
		num /= 10; // 求下一位数字 
		
	} 
	// 最后得出的j,就知道有多少位了 
	// 枚举状态 
	// 1 << j 即二进制 左移 就是乘,右移即除,代表2^j个状态 
	for (int i = 0; i < 1 << j; ++i) {
		int p = i; // 取出i(十进制) 
		sum = 0;
		//转换成二进制(状态),并且与位置上的数字结合
		// p % 2 就是从最低位开始,取二进制数 010101... 
		// k = 0,也是最低位
		// 求和 1 代表染色,0代表不染色,染色就把和加上 
		for (int k = 0; k < j; ++k) {
			sum += (p & 1) * zt[k];
			p = p >> 1; // 求下一位 
		} 
		if (sum * 2 == ans) {
			cout << "Yes";
			return 0;
		} 
	} 
	cout << "No";
	
	return 0;
}

(4)代码(DFS)

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll sum;
ll x;
int getSum (ll x) {
	int res = 0;
	while (x) {
		res += x % 10;
		x /= 10;
	}
	
	return res;
} 
int judge = 0; // 找到一个方案数,正好染色的数字之和等于总和的一半,就为true 

//x 代表当前这个数字(后面几个已经选过了,还有哪些位置还没有选,
//tmp 代表选了后面几位,我会选到什么 (已经选到的数字的和)
void dfs(ll x, ll tmp) { //12345 取 13
	
	if (x == 0 || tmp * 2 >= sum) {
		if (tmp * 2 == sum) {
			judge = 1;
		}
		return ;
	}
	
//	当前x%10的数不取,直接到下一个数
	dfs(x / 10, tmp); 
//	当前x%10的数取
	dfs(x / 10, tmp + x % 10);
	
}
int main () {
	cin >> x; // 输入的数字
	sum = getSum(x); // 求位置上的数字的和 
	dfs(x, 0);
	if (judge) cout << "Yes";
	else cout << "No";
	return 0;
}