2022暑期杭电第十场

这篇博客详细介绍了杭电算法比赛中涉及的三道题目:1003题Wavy Tree,通过分析得出贪心策略来使数组起伏不平;1007题Even Tree Split,通过深搜计算偶数节点数量来确定删边方案;1009题Painting Game,分析A和B的最佳策略形成7的循环节,进而解决涂色问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1003题

Wavy Tree

题目链接

题目大意:

如果对于一个长度为 n n n ,下标从 1 1 1 开始的数组 a a a ,对于 ∀ i ∈ ( 1 , n ) \forall i \in (1, n) i(1,n), 有 a [ i ] > m a x ( a [ i − 1 ] , a [ i + 1 ] ) a[i] > max(a[i - 1], a[i + 1]) a[i]>max(a[i1],a[i+1]) 或者 a [ i ] < m i n ( a [ i − 1 ] , a [ i + 1 ] ) a[i] < min(a[i - 1], a[i + 1]) a[i]<min(a[i1],a[i+1]) 成立,我们认为 a a a 数组是起伏不平的。

现在,给定一个长度为 n n n 且由正整数组成的数组 b b b ,你需要通过一下操作让数组 b b b 变得起伏不平:

  • 选定一个位置 i i i,让 b i b_i bi 增大或者减小 1 1 1

以上操作每次需要消耗 1 1 1 个硬币,问:要使得数组 b b b 变得起伏不平,最少需要耗费多少个硬币?

思路:

通过分析,我们知道:当我们确定了前三个数的相对关系后,后续的所有数的相对关系也随之确定,代价的计算方式也是相同的。

换而言之,我们需要决定的是奇数位置填较大的数还是偶数位填较大的数。直观的看,若将处理后的 b b b 数组在二维坐标系上表示出来,并连线,就是一条波浪线,所以称数组 b b b 起伏不平。

通过分析,对于每三个数,我们注意到:我们若只修改后两个数字,代价将会更低。由于我们修改最后一个数时,只涉及到与第二个数之间的相对关系,换而言之,每次我们只需要贪心的考虑 b i − 1 b_{i - 1} bi1 b i b_i bi 两个数之间的大小关系即可,每次只修改 b i b_i bi ,就能使得最终答案最优。

参考代码:

#include <iostream>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
ll b1[N], b2[N];
ll sum = 0;

void cal(ll a[], int i, bool flag) {
	if(flag) {
		if(a[i - 1] <= a[i])
			sum += a[i] - a[i - 1] + 1, a[i] = a[i - 1] - 1;
	} else {
		if(a[i - 1] >= a[i])
			sum += a[i - 1] - a[i] + 1, a[i] = a[i - 1] + 1;
	}
}

void solve() {
	int n; cin >> n;
	for(int i = 1; i <= n; i ++ ) {
		cin >> b1[i]; b2[i] = b1[i];
	}
	ll ans = 2e18; sum = 0;
	for(int i = 2; i <= n; i ++ )
		if(i & 1) cal(b1, i, 1);
		else cal(b1, i, 0);

	ans = min(ans, sum);
	sum = 0;
	for(int i = 2; i <= n; i ++ )
		if(i & 1) cal(b2, i, 0);
		else cal(b2, i, 1);

	ans = min(ans, sum);
	cout << ans << endl;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	int t; cin >> t;
	while(t -- ) {
		solve();
	}
	return 0;
}

1007题

Even Tree Split

原题链接

题目大意

给定一个长度为 n n n 的无向树,你需要删除其中的一些边,使得剩下的组件的节点数为偶数,求满足条件的删法数量

思路

对整个树进行深搜并计算相对应的节点数量,记录偶数节点的数量为 c n t cnt cnt ,那么答案就是 2 c n t − 1 2 ^ {cnt} - 1 2cnt1

代码

#include<iostream>
#include<string.h>
#include<vector>
#define int long long
using namespace std;
const int N = 1e5+50;
const int mod = 998244353;
vector<int> g[N];
int cnt; 
int sum[N];
void dfs(int u,int fa){
    int len = (int)g[u].size();
    sum[u] = 1;
    for(int i = 0;i < len;i ++){
        int v = g[u][i];
        if(v == fa)continue;
        dfs(v,u);
        sum[u] += sum[v];
    }
    if(sum[u] % 2 == 0 && u != 1)cnt++;
}


void solve(){
    memset(sum,0,sizeof sum);
    int n;
    cin>>n;
    for(int i = 1;i <= n;i ++)g[i].clear();
    for(int i = 0;i < n-1;i ++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    cnt = 0;
    dfs(1,0);
    int ans = 1;
    for(int i = 1;i <= cnt;i ++){
		ans = ans * 2 % mod;	
	}
	cout<<ans-1<<endl;
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin>>t;
    while(t--)solve();
    system("pause");
    return 0;
}

1009题

Painting Game

原题链接

题目大意

A A A B B B 轮流涂色,保证不能有两个相邻格子上色,且一直行动到无法在任何一个格子涂色为止, A A A 想让涂色最小化, B B B 想让涂色最大化,求最后上色的个数。

分析

由于需要一直涂色直到一方无法涂为止,所以在双方都采取最佳策略的情况下, A A A 的策略是在最右涂色位置的三格之后涂色,以确保中间两格一定不可使用; B B B 的策略时在最右涂色位置的四格后涂色,以确保中间的格子必须涂上,所以形成了一个 7 7 7 的循环节,每 7 7 7 个格子中有 3 3 3 个涂满。这样只需考虑小于 7 7 7 的情况,枚举一下即可。

代码

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

typedef long long ll;
typedef double db;

int T,n;
string str;
int a[7]={0,1,1,1,2,2,3},b[7]={0,1,1,2,2,3,3};

int main()
{
    scanf("%d\n",&T);
    while(T--)
    {
        scanf("%d ",&n);
        cin>>str;
        int ans=n/7*3;
        if(str[0]=='A') ans+=a[n%7];
        else ans+=b[n%7];
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值