B、小红的正整数
听了讲解有了另一种方式,适用于更大范围。在这里补充:利用桶排序。
桶排序是假设待排序的一组数均匀独立的分布在一个范围中,并将这一范围划分成几个子范围(桶)。
然后基于某种映射函数f ,将待排序列的关键字 k 映射到第i个桶中 (即桶数组B 的下标i) ,那么该关键字k 就作为 B[i]中的元素 (每个桶B[i]都是一组大小为N/M 的序列 )。
接着将各个桶中的数据有序的合并起来 : 对每个桶B[i] 中的所有元素进行比较排序 (可以使用快排)。然后依次枚举输出 B[0]….B[M] 中的全部内容即是一个有序序列
#include<bits/stdc++.h>
using namespace std;
int tong[10];//数组下标代表0~9数字,数组的值代表每个数字出现的次数
int main(){
string s;
cin>>s;
for(auto i:s) tong[i-'0']++;//存入每个数字出现的次数
int i;
for(i=1;i<=9;i++){//跳过0,把最小的非零正整数输出
if(tong[i]) break;
}
cout<<i;
tong[i]--;//让这个数出现次数减一
for(i=0;i<=9;i++){
while(tong[i]--) cout<<i;
}
}
C、小红的构造回文
回文串:从左往右读和从右往左读一样
思路:可以从中间分开,从左边找到两个不同位置且字符不同,交换一下;右侧在对应位置也进行交换,最后也是回文串。如果从中间开始,左侧元素相同,这就意味着无论怎样交换都是一样的。
关于找对称的思路:对称问题取决于下标是从0~n-1还是1~n;以0~n-1为例:
0与n-1相对;1与n-2相对;2与n-3相对······,他们的和始终为i-1,则i与n-1-i相对。
#include<bits/stdc++.h>
using namespace std;
int main(){
int i;
string s;
cin>>s;
int len=s.length();
for(i=1;i<len/2;i++){//遍历左半边
if(s[i]!=s[i-1]){
swap(s[i],s[i-1]);
swap(s[len-1-i],s[len-1-(i-1)]);
cout<<s;
return 0;
}
}
cout<< -1;
}
D、小红的整数操作
思路: 基本思路:首先利用除法将x,y除到最小,然后再进行乘法,将x,y增大至[l,r]的区间里去。通过将改变小的x,y确定范围,寻找最大乘多少,最小乘多少,确定答案。
(前提:若x<y),最小情况是:x*a>=l,最大情况是y*a<=r。
扩展:使用 __gcd( int x1, int x2 ) 函数可以高效、迅速得到x1, x2两个数的最大公因数。这个函数在<algorithm>库中
数学法:
#include<bits/stdc++.h>
using namespace std;
int gcd1(int x,int y){//手动求最大公约数
if(x%y==0) return y;
return gcd1(y,x%y);
}
int main(){
int x,y,l,r;
cin>>x>>y>>l>>r;
int g=gcd(x,y);//求最大公约数
x/=g;
y/=g;
if(x>y) swap(x,y);
/*
通过举例推数学公式:
要求:x*a>=l;y*a<=r;
例:设x或y为3,l和r为10;
故当求a时,l/x需要向上取整,r/y需要向下取整
*/
int a=l/x+(l%x!=0);
int b=r/y;
cout<<max(0,b-a+1);
return 0;
}
二分法有错误,但没看出来,只过了80%的数据,有能力了回头看看
#include<bits/stdc++.h>
using namespace std;
int main(){
int x,y,l,r;
cin>>x>>y>>l>>r;
int g=gcd(x,y);//求最大公约数
x/=g;
y/=g;
if(x>y) swap(x,y);
/*
要求:x*a>=l;y*a<=r;
*/
int xi=0,da=l;
while(xi<da){
int mid=xi+da>>1;
if(x*mid>=l) da=mid;
else xi=mid+1;
}
int x2=0,d2=r;
while(x2<d2){
int mid2=x2+d2+1>>1;
if(y*mid2<=r) x2=mid2;
else d2=mid2-1;
}
printf("%d",x2-xi+1);
return 0;
}
E、小红树上染色
这是个经典题,特此放在这里作为记录。
相当于取若干个不相邻的数有多少种不同的取法 ,(树形DP问题),类似于不相邻取数牛客竞赛可搜。
#include<bits/stdc++.h>
using namespace std;
int mod=1e9+7;
long long dp[101010][2];//dp[i][0]代表i号节点不染红,dp[i][1]代表i号结点染红
//建树方法
vector<int> g[202020];
int vis[202020];
void dfs(int x,int pr){//pr代表父代
dp[x][0]=dp[x][1]=1;
for(auto i:g[x]){
if(i==pr) continue;//当遍历的邻点正好是父亲就只能向下
dfs(i,x);
dp[x][0]=dp[x][0]*dp[i][1]%mod;//如果点不染红,那他的孩子必须染红
dp[x][1]=dp[x][1]*(dp[i][0]+dp[i][1])%mod;//如果点染红,那么他的孩子可染可不染
}
}
int main(){
int n,i;
cin>>n;
for(i=1;i<n;i++){
int x,y;
cin>>x>>y;
g[x].push_back(y);//把每个点的邻点存到该点的数组里
g[y].push_back(x);
}
dfs(1,0);//默认1号点为根,且根没有父代,所以为0
cout<<(dp[1][0]+dp[1][1])%mod;
}