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 25∗4=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] n−a[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 a−t1×k=b−t2×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 (a−b) mod k=0
k k k一定为 a b s ( a − b ) abs(a-b) abs(a−b)中的因数!
我们随意找两个数 a , b a,b a,b,枚举 a b s ( a − b ) abs(a-b) abs(a−b)的因数一一验证即可
#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(r−b)最小的
至于解决方案的输出,我们追踪 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";
}
}
}