Daliy Algorithm (博弈,位运算,并查集)-- day 86

Nothing to fear


种一棵树最好的时间是十年前,其次是现在!

那些你早出晚归付出的刻苦努力,你不想训练,当你觉的太累了但还是要咬牙坚持的时候,那就是在追逐梦想,不要在意终点有什么,要享受路途的过程,或许你不能成就梦想,但一定会有更伟大的事情随之而来。 mamba out~

2020.6.3


人一我十,人十我百,追逐青春的梦想,怀着自信的心,永不言弃!

Game on leaves

为什么会有如此结论?
当 n 为奇数的时候后手赢
当 n 为偶数的时候先手赢

我们到这思考这个问题,
假设As要赢 那么此时他面对的情况一定是 一个x结点连接着另外一个
结点,因为如果不是此时 x 的度 > 1 是不能移除的。
此时他前面那个人一定是把 和 x 相连的另一个结点给拿走
否则他可以直接移除 x 结点。
那么我们的问题就变成了 当只剩下 x结点和与之相邻的一个结点时
当前人是谁

于是我们把这一部分单独划分出来 之前的数字就变成了 n - 2
也就是说当剩下那一部分的个数为偶数的时候那么此时一定是先手面对
最后的x
但是如果剩下的结点是偶数的话那么一定是后手赢得比赛由于加上2之后
对于其原本的奇偶性并不改变 所以我们只需要直接判断 n 的奇偶性即可

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <cassert>
#include <string>
#include <iomanip>
#include <cmath>
#include <ctime>
#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define lowbit(x) (x & -x)
using namespace std;
typedef long long ll;
const int MAX = 0x7ffffff;
const int N = 1005;
int t;
int n , x;
int degree[N]; // 记录每一个结点的度
/*
*/
void slove()
{
	memset(degree , 0 , sizeof degree);
	cin >> n >> x;
	int a,  b;
	for(int i = 1;i <= n - 1;i ++)
	{
		cin >> a >> b;
		degree[a]++ , degree[b]++;
	}
	if(degree[x] <= 1)cout << "Ayush" << endl;
	else{
		if(n % 2)
		{
			cout << "Ashish" << endl;
		}else cout << "Ayush" << endl;
	}
}
int main()
{
#ifdef LOCAL
	auto start_time = clock();
	cerr << setprecision(3) << fixed; // 在iomanip中
#endif
	SIS;
	cin >> t;
	while(t--)
	{
		slove();
	}
#ifdef LOCAL
	auto end_time = clock();
	cerr << "Execution time: " << (end_time - start_time) * (int)1e3 / CLOCKS_PER_SEC << " ms\n";
#endif
}

哈密顿路径

利用状态压缩降低np hard问题的时间复杂度 虽然仍然很高但是已经可以承受小数据集。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <vector>
#include <cstring>
using namespace std;
const int N = 20;
int f[1 << N][N] , weight[N][N];
int n;
void slove()
{	
	cin >> n;
	for(int i = 0;i < n ;i ++)
		for(int j = 0;j < n ;j ++)
			cin >> weight[i][j];

	memset(f , 0x3f , sizeof f);
	f[1][0] = 0;
	for(int i = 1;i < 1 << n ;i ++)
		for(int j = 0;j < n ;j ++)
		{
			if((i >> j) & 1)
			{
				for(int k = 0;k < n ;k ++)
				{
					if((i ^ 1 << j) >> k & 1)
					f[i][j] = min(f[i][j] , f[i ^ 1 << j][k] + weight[k][j]);
				}
			}
		}
	cout << f[(1 << n) - 1][n-1];
}

int main()
{
	slove();
	return 0;
}

起床困难综合症

位运算结合贪心的经典方法,适合反复食用 这次就先不写题解了 懒

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <string>

using namespace std;

const int N = 1000005;

pair<string,int> a[N];
int n , m;
/*
由于位运算的独立性我们可以逐个确定每一位是0/1
bit表示当前是哪一位,now 表示当前是0 / 1
确定某一位选 1 / 0 是否可行
*/
int cal(int bit,int now)
{
	for(int i = 1;i <= n ;i ++)
	{
		//取出参数 t 的第 bit位进行运算 
		int x = a[i].second >> bit & 1;
		if(a[i].first == "AND") now &= x;
		if(a[i].first == "OR") now |= x;
		if(a[i].first == "XOR")now ^= x;
	}
	return now;
}	
int main()
{

	cin >> n >> m;
	for(int i = 1;i <= n ;i ++)
	{
		string s;int x;
		cin >> s >> x;
		a[i] = make_pair(s , x);
	}

	int val = 0, ans = 0;
	for(int bit = 29 ; bit >= 0; bit--)
	{
		int res0 = cal(bit , 0);
		int res1 = cal(bit , 1);
		if(val + (1 << bit) <= m && res0 < res1)
		{
			val += 1 << bit;
			ans += res1 << bit;
		}else ans += res0 << bit;
	}

	cout << ans << endl;
	return 0;
}

L1-020 帅到没朋友

简单并查集问题

将问题抽象为检查集合中的不同元素的个数
设置一个并查集将不用集合的元素放入其中
cnt用来当前以 i 为根节点的集合的总元素的个数

我们初始化并查集和cnt数组cnt[i] = 1;

当我们查询的时候只需要查找该结点的根节点的子树结点的个数
即cnt[get(query)] 如果其等于 1 表示是个自恋少年,否则有朋友

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <vector>
#include <cstring>
using namespace std;
const int N = 1000000;
int f[N] , cnt[N];
int n , m;
int arr[1005];
bool vis[N];
// 查
int get(int x)
{
	if(x == f[x])return x;
	return f[x] = get(f[x]);
}
// 并
void merge(int x,int y)
{
	int a = get(x);
	int b = get(y);
	f[a] = b; // x 的树根作为 y 的树根的子节点
	cnt[b] += cnt[a];
}

void slove()
{	
	for(int i = 0;i <= 99999 ;i ++)f[i] = i , cnt[i] = 1;
	cin >> n;
	for(int i = 1;i <= n ;i ++)
	{
		int k;cin >> k;
		for(int i = 1;i <= k ;i ++)
		{
			scanf("%d",&arr[i]);
		}
		// 进行合并
		for(int i = 1;i < k ;i ++)
		{
			int x = get(arr[i]);
			int y = get(arr[i+1]);
			if(x != y)
			{
				merge(arr[i] , arr[i + 1]);
			}
		}
	}
	cin >> m;
	int query = 0 , time = 0;
	for(int i = 1;i <= m ;i ++)
	{
		scanf("%d",&query);
		if(cnt[get(query)] == 1 && vis[query] == 0)
		{
			if(time == 0)printf("%05d",query);
			else printf(" %05d",query);
			time++;vis[query] = 1;
		}
	}
	if(!time)cout << "No one is handsome";
	return;
	cout << "简单明了" << endl;
}

int main()
{
	slove();
	return 0;
}
posted @ 2020-06-03 18:24  _starsky  阅读(131)  评论(0编辑  收藏  举报