目录
问题 A: 计算矩阵边缘元素之和
模拟
void solve()
{
int n,m;
cin>>n>>m;
int ans = 0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int e;
cin>>e;
if(i==n || i==1 || j==1 || j==m) ans+=e;
}
}
cout<<ans;
}
问题 B: 图像旋转
注意:输入矩阵不一定都是方阵
int a[105][105];
void solve()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
cout<<a[n-j+1][i]<<" ";
}
cout<<endl;
}
}
问题 C: 最长最短单词
题目难处理的地方在于还有逗号分割,不可以直接用cin读入。具体做法为一直更新最大(小)单词长度和他们最后一个字符的位置即可
void solve()
{
string s;
getline(cin,s);
int len = s.length();
int minn = 2e9,maxn = -1;
int idx_max,idx_min;//最大最小值下标
int now = 0;
for(int i=0;i<len;i++){
if(s[i]==' '||s[i]==','){
if(now>maxn){
maxn = now;
idx_max = i;
}
if(now<minn){
minn = now;
idx_min = i;
}
now = 0;
}else{
now++;
}
}
for(int i=idx_max-maxn;i<idx_max;i++) cout<<s[i];
cout<<endl;
for(int i=idx_min-minn;i<idx_min;i++) cout<<s[i];
}
问题 D: 输出亲朋字符串
模拟
void solve()
{
string s;
cin>>s;
int len = s.length();
for(int i=0;i<len-1;i++){
cout<<char(s[i]+s[i+1]);
}
cout<<char(s[0]+s[len-1]);
}
问题 E: [蓝桥杯2022初赛] 李白打酒加强版
(1)朴素版dfs
n+m最大能达到200,如果直接用dfs暴力会超时
直接写dfs思路很好想,但是会超时,复杂度为O(2^n)
int n,m;
int ans;
void dfs(int u,int cnt,int a,int b){
//u为当前酒还剩多少升 cnt为当前层数 a为选了几个店,b为选了几次花
if(a>n || b>m) return ;
if(cnt==n+m-1){ //最后一次必须为花,且酒需要剩下一升
if(u == 1 && a==n && b==m-1) ans++;
return ;
}
if(u<=0) return ;
dfs(u*2,cnt+1,a+1,b);
dfs(u-1,cnt+1,a,b+1);
}
void solve()
{
int t;
cin>>t;
while(t--){
cin>>n>>m;
ans = 0;
dfs(2,0,0,0);
cout<<ans%mod<<endl;
}
}
想用dfs不超时的话就需要用到记忆化搜索和剪枝了,减少不必要的递归,假设u是酒量,剪枝之后 u 的值又不会大于 m
1.题目限定最后一次遇到的是花且遇到花时一定有酒,那么当u=0时,n,m一定也为0,(n=0是因为最后一次遇花,m=0是因为遇到必须要有酒)
2.当然,如果u>m时,说明酒喝不完,就满足不了题目提到的酒正好喝完这个条件
3.同理,n>=m时也不符合题意,因为酒的增长速度是指数级别的,而遇到花喝酒,酒的减少速度是线性的
(2)dfs+记忆化搜索+剪枝
int vis[100+5][100+5][100+5];
ll dfs(int n,int m,int u){
//u为当前酒还剩多少升 a为剩几个店可以选择,b为剩多少花能选
if(u==0) return (n==0 && m==0);
if(u>m || n>=m) return 0;
if(n==0) return u==m;
if(vis[n][m][u]!=-1) return vis[n][m][u];
ll ans = dfs(n,m-1,u-1) + dfs(n-1,m,u*2);
ans %= mod;
vis[n][m][u] = ans;
return ans;
}
void solve()
{
int t;
cin>>t;
while(t--){
memset(vis,-1,sizeof vis);
int n,m;
cin>>n>>m;
cout<<dfs(n,m,2)<<endl;
}
}
问题 F: 【蓝桥杯2022初赛】扫雷
dfs+哈希表
const int M = 999997;//大于n+m的100000数据量够用了
const int base = 1e9 + 1;//保证坐标不会重复
int myhash[M];//散列表
int cnt[M];//该点有几个炸弹
int maxr[M];//该点炸弹的最大半径
bool vis[M];//该点炸弹是否已经引爆
int ans;
int getkey(int x,int y)
{
//坐标转移成一个数
return x*base+y;
}
int findaddress(int x)
{
//返回数值x在散列表中的索引
int t = (x%M+M)%M;
while(myhash[t]!=-1 && myhash[t]!=x){//第一个条件针对存放值+第二个条件针对取值
t++;
if(t==M) t=0;
}
return t;
}
bool dist(int x1,int y1,int x2,int y2,int r)
{
return (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)<=r*r;
}
void dfs(int x,int y,int r)
{
for(int i=x-r;i<=x+r;i++){
for(int j=y-r;j<=y+r;j++){
int key = getkey(i,j);
int idx = findaddress(key);
if(cnt[idx] && !vis[idx] && dist(i,j,x,y,r)){
ans+=cnt[idx];
vis[idx] = true;
dfs(i,j,maxr[idx]);
}
}
}
}
void solve()
{
int n,m;
cin>>n>>m;
memset(myhash,-1,sizeof myhash);
for(int i=0;i<n;i++){
int x,y,r;
cin>>x>>y>>r;
int key = getkey(x,y);
int idx = findaddress(key);
myhash[idx] = key;
cnt[idx]++;
maxr[idx] = max(r,maxr[idx]);
}
for(int i=0;i<m;i++){
int tx,ty,tr;
cin>>tx>>ty>>tr;
dfs(tx,ty,tr);
}
cout<<ans<<endl;
}
问题 G: 今天猛犸不上班
和求某个数在n进制下一样的求法
void solve()
{
int n,m;
cin>>n>>m;
int digit = 0;
while(n){
n/=m;
digit++;
}
cout<<digit;
}
问题 H: 你干嘛 哈哈嗨呦
如果人数是奇数,公鸡摆放最优的位置是在中间人的位置上;如果人数是偶数,最优的位置在中间两个人坐标的平均数、平均数-1两个数中的一个(int默认向下取整了)
const int N = 100+5;
int a[N];
void solve()
{
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+n+1);
int ans = 0;
if(n&1){
int x = a[n/2+1];
for(int i=1;i<=n;i++) ans+=(x-a[i])*(x-a[i]);
}else{
int x1 = (a[n/2]+a[n/2+1])/2;
int x3 = (a[n/2]+a[n/2+1])/2-1;
int ans1 = 0,ans2 = 0,ans3 = 0;
for(int i=1;i<=n;i++){
ans1+=(x1-a[i])*(x1-a[i]);
ans3+=(x3-a[i])*(x3-a[i]);
}
ans = min(ans1,ans3);
}
cout<<ans<<endl;
}
问题 I: 打印文件
也是涉及取整的问题
void solve()
{
double n;
cin>>n;
cout<<(int)(n/2+0.5);
}
问题 J: 群聊交友
可以把这题抽象成一个图论问题。
N个群友视为n个独立的点,之间的联系通过建双向边完成。
逐条翻译条件:
(1)图中不存在自环
(2)在朋友关系的图中,i,j之间不存在边
(3)在黑名单关系的图中,i,j之间不存在边
(4)i,j是在一个连通分量中的
1/2/3条件比较容易实现,存储图即可,条件4查找两个点是否在同一个连通分量中,可以使用并查集+启发式合并实现
const int N = 100000+10;
int n,m,k;
int p[N];//记录祖宗节点
int _size[N];//记录联通分量中点的个数
vector<int>g1[N],g2[N];//g1为朋友,g2为黑名单
int find(int x) //查找祖宗节点
{
if(x==p[x]) return x;
return p[x] = find(p[x]);
}
void solve()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++) p[i] = i,_size[i] = 1;
for(int i=0;i<m;i++){
int l,r;
cin>>l>>r;
g1[l].push_back(r);
g1[r].push_back(l);
int fa = find(l);
int fb = find(r);
if(_size[fa]<_size[fb]) swap(fa,fb);
if(fa!=fb){
_size[fa] += _size[fb];
p[fb] = fa;
}
}
for(int i=0;i<k;i++){
int l,r;
cin>>l>>r;
g2[l].push_back(r);
g2[r].push_back(l);
}
for(int i=1;i<=n;i++){
int t = find(i);
int res = _size[t]-1;//连通分量中有几个朋友
//cout<<res<<endl;
for(auto it:g1[i]){//去除在连通分量中是朋友的
if(find(it)==t) res--;
}
for(auto it:g2[i]){//去除在连通分量中是黑名单的
if(find(it)==t) res--;
}
cout<<res<<" ";
}
}
问题 K: 字符串查询
这题和最近cf中一场Div4里面的题目很像。原题链接:Problem - F - Codeforces
我们可以将每个小写字母出现的下标存储在set中,计算区间中有多少种字母时,只需要计算有多少个小写字母的下标在区间范围内即可,可以用二分查找下标位置。
例如:set['z'-'a']中有元素1,4,8,代表s[1],s[4],s[8]的元素是小写字母z
操作1:将下标加入对应字母的set容器中
操作2:将被修改的字母从对应容器中删去,修改后的字母加入对应字母的容器中
const int N = 500000+10;
char s[N];
set<int>se[26+10];
void solve()
{
int n;
cin>>n>>s+1;
for(int i=1;i<=n;i++) se[s[i]-'a'].insert(i);
int q;
cin>>q;
while(q--){
int op;
cin>>op;
if(op==1){
int idx;
char c;
cin>>idx>>c;
if(c!=s[idx]){
se[s[idx]-'a'].erase(idx);
se[c-'a'].insert(idx);
s[idx] = c;
}
}else{
int l,r;
cin>>l>>r;
int res = 0;
for(int i=0;i<26;i++){
auto t = se[i].lower_bound(l);//找到第一个大于等于l的位置
if(t!=se[i].end() && *t<=r) res++;
}
cout<<res<<endl;
}
}
}
问题 L: X老师的多米诺骨牌
Orz,源于出题人的题解(周赛的题)
思路:首先,将 pair根据 Xi 的大小进行排序。然后我们考虑动态规划,当只考虑第 i 个骨牌和 i 之后的骨牌时,可能的剩余骨牌状态数。那么,如果骨牌 i 没有被 推到,那么可能的状态数为 dp[i+1];如果骨牌 i 被推倒那么可能的状态数为 dp[Ri]( Ri= 只有骨牌 i 推到后,大于 i 中最小的未被推到的骨牌的编号(如果没有则 Ri=n+1)),所以 dp[i]=dp[i+1]+dp[Ri](dp[n+1]=1)。之后我们可以使用单调队列优化 dp,i 从 n 到 1 得出 Ri;或者用二分+求区间最大值也可以求出 Ri。
const int N = 2e5+10;
const int mod = 998244353;
int n;
PII a[N];
int dp[N];
stack<PII>sta;
void add(int &x,int y)
{
x+=y;
if(x>=mod) x-=mod;
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].first>>a[i].second;
sort(a+1,a+1+n);
dp[n+1] = 1;
for(int i=n;i>=1;i--){
int tmp = a[i].first+a[i].second;
int R = i+1;
while(!sta.empty() && tmp>a[sta.top().first].first){
R = sta.top().second;
sta.pop();
}
sta.push({i,R});
dp[i] = dp[i+1];
add(dp[i],dp[R]);
}
cout<<dp[1];
}