8.18 杭电杯多校联赛第十场

本文探讨了博弈论中关于格子涂色的问题,分析了A和B交替涂色的最优策略,讨论了先手优势如何影响涂色结果,并通过代码展示了两种情况下的计算方法:A先手和B先手时涂色格子数量的确定。涉及到了DFS和快速幂等算法技巧。
摘要由CSDN通过智能技术生成

博弈论 - 格子涂色

题意

见代码

思路

见代码

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;

// 博弈论
// 一行有n个位置 A和B轮流涂色 相邻两个位置不可以都涂色 直到某人不可涂色 游戏结束
// A想让涂色的格子数量尽可能少 B想让涂色格子数量尽可能多
// 两个人都采取最优策略 给定格子数n以及先手是谁 问游戏结束时涂色的格子数量是多少?

// A由于要尽可能使得涂色少 即通过A的涂色 卡死一些格子 让这些格子不可涂
// B要尽可能多 那就是B要使得有一些格子必须涂, 即B涂完某个位置, 让这个位置使得另外的格子最终也会涂色

// 从端点前面几个位置开始分析
// 1 2 3 .... 假如 A先涂色  那么A一定会涂2 这样1 3都没办法涂
// 假如B先涂色 那么B一定涂在3  这样可以使得1必须涂


// A先手的情况
// 一开始涂第二格 使得两边不能涂  1 A 3   
// 然后B会涂在6使得4必须涂        1 A 3 4 5 B
// 然后A会涂在9使得8不能涂   	  1 A 3 4 5 B 7 8 A
// 然后B会涂在13使得11必须涂      1 A 3 4 5 B 7 8 A 10 11 12 13
// ..............出现了循环 即 0 A 0 X 0 B 0 一个循环 即每7位 都有3个位置一定会涂色

// 考虑剩下的长度即可
// 0 A 0 X 0 B 0 K1 K2 K3 K4 K5 K6
// 剩下1: 答案就是 n/7 * 3 + 1  A涂了K1
// 剩下2: 答案就是 n/7 * 3 + 1  最后A会涂在K2 使得K1不能涂
// 剩下3: 答案就是 n/7 * 3 + 1  和上面一样 A涂在K2 使得K1 K3 不能涂
// 剩下4: 答案就是 n/7 * 3 + 2  还是选择涂在K2 但是K4会被B涂
// 剩下5: 答案就是 n/7 * 3 + 2  还是A涂在K2 B涂在4或者5都一样
// 剩下6: 答案就是 n/7 * 3 + 3  A涂K2 B涂K6使得K4必须涂


// B先手的情况
// 一开始涂3 使得 1 必须涂, 即 1 2 B 4 5 6 7 8 9 10
// 然后A涂在6使得5不能涂        1 2 B 4 5 A 7 8 9 10
// 然后B涂在10使得8必须涂       1 2 B 4 5 A 7 8 9 B
// 然后                         1 2 B 4 5 A 7 8 9 B 11 12 A 14 ....
// 循环体: X 0 B 0 0 A 0 同样是每7个都会有 3个位置涂色
// 同样考虑剩下的  X 0 B 0 0 A 0 K1 K2 K3 K4 K5 K6
// 剩1: B涂在K1   +1
// 剩2: B涂在K1   +1
// 剩3: B涂在K3使得K1必须涂 +2
// 剩4: B涂在K3使得K1必须涂 +2
// 剩5: B涂在K3使得K1必须涂 然后A要涂在K5  +3
// 剩6: B涂在K3使得K1必须涂 然后A涂在K6使得K5不能涂  +3

// 我们还发现 两种循环体分别是 0 A 0 X 0 B 0 和 X 0 B 0 0 A 0
// 由于两种情况最后一位都没有填  所以前面的循环体对于后面剩余的怎么填没有任何影响
// 即我们对剩余的分析过程 又可以恰好用于 对 n<7 时候的分析

ll n, res;
int main()
{
	int t;
	cin>>t;
	string name;
	while(t--){
		cin>>n>>name;
		res = n / 7 * 3;
		if(n % 7 == 0){
			cout<<res<<endl;
			continue;
		}
		
		if(name == "Alice"){
			if(n % 7 <= 3) res += 1;
			else if(n % 7 <= 5) res += 2;
			else res += 3;
		}
		
		if(name == "Bob"){
			if(n % 7 <= 2) res += 1;
			else if(n % 7 <= 4) res += 2;
			else res += 3;
		}
		
		cout<<res<<endl;
	}
}

树DFS,快速幂

题意

见代码

思路

见代码

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 1e5 + 5;
const int mod = 998244353;

int res, n;
int sz[N];
int h[N], ne[N*2], e[N*2], idx;

// 题意 : n为偶数  给定n个结点 以及n-1条边 构成一棵树  问:有几种删法,可以删掉某些边使得, 剩下的子连通图的顶点数量都是偶数
// 对于某个结点 只要以他为根的子树结点数量是偶数 ,他头上的边一定可以删掉, 根节点除外
// 假如子树结点数是偶数的点有res个 所以最后答案就是 2^(res-1) - 1  减1因为:全都不删不是一种选择

int qmi(int a, int zhi)
{
	int ans = 1;
	while(zhi){
		if(zhi & 1) ans = (ll)ans * a % mod;
		zhi >>= 1;
		a = (ll)a*a % mod;
	}
	return ans % mod;
}

void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int i, int fi)
{
	sz[i] = 1;
	for(int t = h[i]; t != -1; t = ne[t]){
		int j = e[t];
		if(fi == j) continue;
		dfs(j, i);
		sz[i] += sz[j];
	}
	if(sz[i] % 2 == 0) res++;
}


int T;
int main()
{
	scanf("%d",&T);
	while(T--){
		memset(h, -1, sizeof(h));
		idx = 0;
		
		scanf("%d", &n);
		
		for(int i = 0; i<n-1; i++){
			int a, b;
			scanf("%d%d", &a, &b);
			add(a, b), add(b, a);
		}
		
		res = 0;
		dfs(1, 0);
		cout<< (qmi(2, res-1) - 1 + mod) % mod<<endl; // 要除去不删的一种情况
	}
}

贪心

题意

给定一个数组,你可以将任意一个元素+1或者-1无限次,使得数组成为波浪型,即升降交替,问最小操作次数。

思路

从结果分析,最终结果是波浪型,也就是奇数位置为峰,偶数位置为谷,或者是偶峰奇谷。对于每一种情况,我们去遍历原数组,一旦违反了峰谷规则,就对当前元素进行修改, 最后两种情况的修改次数取最小值即可。

代码

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GTTwelve

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值