Codeforces Round #748 (Div. 3)

本文探讨了五种编程问题解决方案,包括分类讨论处理最大值特殊情况、利用BFS优化百位以上数字操作、贪心策略分配资源、数论技巧解决同余条件和枚举+数论的组合优化。展示了如何运用不同的算法思想解决实际编程挑战。

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

A.分类讨论

要注意多个数值相等且都为最大值的情况

此时的答案为最大值减去当前值+1

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

int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		int a,b,c;cin>>a>>b>>c;
		int aa = max({a,b,c});
//		cout<<aa<<endl;
		if (aa>b&&aa>c)
		{
			cout<<aa-a<<" ";
		}else cout<<aa+1-a<<" ";
		if (aa>a&&aa>c)
		{
			cout<<aa-b<<" ";
		}else cout<<aa+1-b<<" ";
		if (aa>a&&aa>b)
		{
			cout<<aa-c<<"\n";
		}else cout<<aa+1-c<<"\n";
	}
}

B.思维+ b f s bfs bfs

因为 25 ∗ 4 = 100 25*4=100 254=100

所以,对于百位即以上的位数,无论我们取什么都可以整除

我们只需关注个位和十位

因此,每次删除要么删除个位上的数,要么删除十位上的数,这样一来枚举的情况就大大减少了

我们 b f s bfs bfs

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int bfs(ll num)
{
	queue<pair<ll,int>> que;
	que.push({num,0});
	map<ll,bool> mp;
	while (!que.empty())
	{
		pair<ll,int> p = que.front();
		que.pop();
		if (p.first<25)continue;
		if (p.first%25==0)return p.second;
		pair<ll,int> pp = {p.first/10,p.second+1};
		pair<ll,int> ppp = {p.first/100*10+p.first%10,p.second+1};
		if (!mp[pp.first])
		{
			mp[pp.first]=1;
			que.push(pp);
		}
		if (!mp[ppp.first])
		{
			mp[ppp.first]=1;
			que.push(ppp);
		}
	}
}
int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		ll num;cin>>num;
		cout<<bfs(num)<<"\n";
	}
}

C.思维+贪心

猫走到老鼠洞总共需要 n n n步,每个老鼠 a [ i ] a[i] a[i]走到老鼠洞总共需要 n − a [ i ] n-a[i] na[i]

取最多的老鼠,使得他们走进老鼠洞的总步数小于 n n n即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e5+100;
int a[maxn];
int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		int n,k;cin>>n>>k;
		for (int i=1;i<=k;++i)cin>>a[i];
		sort(a+1,a+1+k);
		int ans = 0;int res = n-1;
		for (int i=k;i>=1;--i)
		{
			if (res-n+a[i]>=0)++ans,res-=n-a[i];
			else break;
		}cout<<ans<<"\n";
	}
}

D1.思维+数论

两个数 a , b a,b a,b,对这两个数执行操作

a a a减去 t 1 t_1 t1 k k k b b b减去 t 2 t_2 t2 k k k

如此 a − t 1 × k = b − t 2 × k a-t_1\times k=b-t_2\times k at1×k=bt2×k

那么 a   m o d   k = b   m o d   k a\ mod\ k=b\ mod\ k a mod k=b mod k

a , b a,b a,b k k k取余的余数一定要相同

那么 ( a − b )   m o d   k = 0 (a-b)\ mod\ k=0 (ab) mod k=0

k k k一定为 a b s ( a − b ) abs(a-b) abs(ab)中的因数!

我们随意找两个数 a , b a,b a,b,枚举 a b s ( a − b ) abs(a-b) abs(ab)的因数一一验证即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[50],n;
bool check(int num)
{
	for (int i=2;i<=n;++i)
	{
		if ((a[i]%num+num)%num!=(a[i-1]%num+num)%num)return false;
	}return true;
}
int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		cin>>n;
		for (int i=1;i<=n;++i)cin>>a[i];
		sort(a+1,a+1+n);
		int big = a[n]-a[1];
		int ans = -1;
		for (int i=1;i*i<=big;++i)if (big%i==0)
		{
			if (i>ans&&check(i))ans = i;
			if (big/i>ans&&check(big/i))ans = big/i;
		}
		cout<<ans<<"\n";
	}
}

D2.枚举+数论

基本策略和D1并没有什么不同

只是这里要求如果有至少一半的数字他们关于 k k k同余即可

如此,我们可以枚举所有的数字对 C 40 2 C_{40}^2 C402

按照 D 1 D1 D1的策略找到所有的 k k k,能满足至少有一半的元素同余

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 1e9;
int a[50],n;
bool check(int num)
{
    map<int,int> mp;
    for (int i=1;i<=n;++i)mp[(a[i]%num+num)%num]++;
    for (auto p:mp)if (p.second >= n/2)return true;
    return false;
}
int solve(int num)
{
    int ans = 0;
    for (int i=1;i*i<=num;++i)if (num%i==0)
    {
        if (i>ans&&check(i))ans = i;
        if (num/i>ans&&check(num/i))ans = num/i;
    }return ans;
}
int main()
{
    ios::sync_with_stdio(0);
    int T;cin>>T;
    while (T--)
    {
        cin>>n;
        for (int i=1;i<=n;++i)cin>>a[i];
        sort(a+1,a+1+n);
        map<int,int> mp;
        for (int i=1;i<=n;++i)mp[a[i]]++;
        int ans = 0;
        for (auto p:mp)if (p.second>=n/2)
        {
            ans = -1;
            break;
        }if (ans==-1)
        {
            cout<<ans<<"\n";
            continue;
        }
        for (int i=1;i<=n;++i)
            for (int j=i+1;j<=n;++j)
                ans = max(ans,solve(a[j]-a[i]));
        cout<<ans<<"\n";
    }
}

E. b f s bfs bfs

一个点可删除,当且仅当他的度数 ≤ 1 \le 1 1

我们记录下所有的度数,从所有度数 ≤ 1 \le 1 1的点开始 b f s bfs bfs即可

保证 b f s bfs bfs的层数 ≤ k \le k k

答案便是 n − n- n所有走过的点数

#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e5+100;
vector<int> G[maxn];
bool de[maxn];
int du[maxn];
int n,k;
int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		cin>>n>>k;
		for (int i=1;i<=n;++i)G[i].clear(),du[i]=de[i]=0;
		for (int i=1,u,v;i<n;++i)
		{
			cin>>u>>v;
			G[u].push_back(v);
			G[v].push_back(u);
			du[u]++;du[v]++;
		}
		queue<pair<int,int>> que;
		for (int i=1;i<=n;++i)if (du[i]<=1)
			que.push({i,1});
		while (!que.empty())
		{
			pair<int,int> p = que.front();
			que.pop();
			if (de[p.first])continue;
			de[p.first]=1;
			if (p.second==k)continue;
			for (int v:G[p.first])
			{
				du[v]--;
				if (du[v]<=1&&!de[v])que.push({v,p.second+1});
			}
		}
		int ans = n;
		for (int i=1;i<=n;++i)if (de[i])--ans;
		cout<<ans<<"\n";
	}
}

F. d p dp dp

d p dp dp问题,看到数据范围很小,刚开始向组合数学、枚举方向去想了想

没有思路。然后就向高维 d p dp dp方向思考,得到了解法

首先,我们的 d p dp dp状态中必然有维度 i d id id,即,现在遍历到了第几个数字

然后每一个数字有两种可能,要么分配到数字 r e d red red,要么分配到数字 b l a c k black black

最终,要保证 r e d   m o d   A = 0 , b l a c k   m o d   B = 0 red\ mod\ A=0,black\ mod\ B=0 red mod A=0,black mod B=0

和余数有关,我们一般可以将余数加入状态中

d p [ i d ] [ r e m A ] [ r e m B ] dp[id][rem_A][rem_B] dp[id][remA][remB]

表示,遍历到 i d id id,然后 r e d   m o d   A = r e m A , b l a c k   m o d   B = r e m B red\ mod\ A=rem_A,black\ mod\ B=rem_B red mod A=remA,black mod B=remB 是否可行

答案就是 d p [ n ] [ 0 ] [ 0 ] dp[n][0][0] dp[n][0][0]

但是,在我们枚举第 i d id id个数字分配给谁时,出现了问题

假设我们将第 i i i个数字分配给 r e d red red,因为我们不知道 r e d , b l a c k red,black red,black的长度,

所以我们不知道第 i i i个数字在 r e d red red中占第几位,这样我们就不知道第 i i i个数字和 A A A取模是多少

所以还要加一维: d p [ i d ] [ l e n ] [ r e m A ] [ r e m B ] dp[id][len][rem_A][rem_B] dp[id][len][remA][remB]

表示,遍历到 i d id id,然后 r e d red red长度位 l e n len len r e d   m o d   A = r e m A , b l a c k   m o d   B = r e m B red\ mod\ A=rem_A,black\ mod\ B=rem_B red mod A=remA,black mod B=remB 是否可行

因此,转移 d p dp dp即可

我们最后枚举 l e n len len,找到 a b s ( r − b ) abs(r-b) abs(rb)最小的

至于解决方案的输出,我们追踪 d p dp dp的转移即可

ps:因为 d p dp dp的转移稍显复杂,所以我选择用 d f s dfs dfs记忆化搜索实现

#include<bits/stdc++.h>
using namespace std;
int pA10[45],pB10[45];
bool dp[45][45][45][45];
bool vis[45][45][45][45];
bool nxt[45][45][45][45];
int a[45];
int n,A,B;
//bool dp[id][len][rem_A][rem_B]
bool dfs(int id,int len,int rem_A,int rem_B)
{
	if (len>id||len<0)return false;
	if (id==0)return rem_A==0&&rem_B==0;
	if (vis[id][len][rem_A][rem_B])return dp[id][len][rem_A][rem_B];
	vis[id][len][rem_A][rem_B]=1;
	int num1;
	int num2;
	if (len==0)
	{
		num2 = a[id]*pB10[id-len-1]%B;
		if (rem_A!=0)return dp[id][len][rem_A][rem_B]=false;
		for (int rem_B_ = 0;rem_B_<B;++rem_B_)
		{
			if((rem_B_+num2)%B==rem_B&&dfs(id-1,len,rem_A,rem_B_))
				return dp[id][len][rem_A][rem_B]=1;
		}return 0;
	}
	if (len==id)
	{
		num1 = a[id]*pA10[len-1]%A;
		if (rem_B!=0)return dp[id][len][rem_A][rem_B]=false;
		for (int rem_A_ = 0;rem_A_<A;++rem_A_)
		{
			if((rem_A_+num1)%A==rem_A&&dfs(id-1,len-1,rem_A_,rem_B))
			{	
				nxt[id][len][rem_A][rem_B]=1;
				return dp[id][len][rem_A][rem_B]=1;
			}
		}return 0;
	}
	num2 = a[id]*pB10[id-len-1]%B;
	num1 = a[id]*pA10[len-1]%A;
	for (int rem_A_ = 0;rem_A_<A;++rem_A_)
	{
		if((rem_A_+num1)%A==rem_A&&dfs(id-1,len-1,rem_A_,rem_B))
		{
			nxt[id][len][rem_A][rem_B]=1;
			return dp[id][len][rem_A][rem_B]=1;
		}
	}
	for (int rem_B_ = 0;rem_B_<B;++rem_B_)
	{
		if((rem_B_+num2)%B==rem_B&&dfs(id-1,len,rem_A,rem_B_))return dp[id][len][rem_A][rem_B]=1;
	}return 0;
}
 
int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		memset(dp,0,sizeof(dp));
		memset(nxt,0,sizeof(nxt));
		memset(vis,0,sizeof(vis));
		cin>>n>>A>>B;
		pA10[0]=pB10[0]=1;
		for (int i=1;i<=40;++i)pA10[i]=pA10[i-1]*10%A,pB10[i]=pB10[i-1]*10%B;
		for (int i=1;i<=n;++i)
		{
			char ch;cin>>ch;
			a[i] = (int)(ch-'0');
		}
		reverse(a+1,a+1+n);
		int ans = 44;
		int l;
		for (int len=1;len<n;++len)
			if (abs(n-len-len)<ans&&dfs(n,len,0,0))
			{
				ans = abs(n-len-len);
				l=len;
			}
		if (ans==44)cout<<"-1\n";
		else
		{
			int len = l;
			int rem_A=0,rem_B=0;
			for (int id=n;id>=1;--id)
			{
				if (nxt[id][len][rem_A][rem_B])
				{
					cout<<"R";
					int num = a[id]*pA10[len-1]%A;
					for (int rem_A_ = 0;rem_A_<A;++rem_A_)if((rem_A_+num)%A==rem_A)
					{
						rem_A = rem_A_;
						break;
					}--len;
				}
				else 
				{
					cout<<"B";
					int num = a[id]*pB10[id-len-1]%B;
					for (int rem_B_ = 0;rem_B_<B;++rem_B_)if((rem_B_+num)%B==rem_B)
					{
						rem_B = rem_B_;
						break;
					}
				}
			}cout<<"\n";
		}
	}
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值