第十四届蓝桥杯省赛Python组真题(未完)

AcWing 4965. 三国游戏 - AcWing

法1:dfs的时间复杂度是2^n 对于每一个我们有选与不选两种 

//法1:dfs
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define x first
#define y second
typedef pair<int, int> PII;

const int N=1e5+7;
int n;
int a[4][N];
int x,y,z; 
int ans=0;
int len=0;
void dfs(int dep){
	if(x>(y+z)||y>(x+z)||z>(y+x)){
		ans=max(len,ans);
	}
	if(dep==n+1)return ;
	x+=a[1][dep];
	y+=a[2][dep];
	z+=a[3][dep];
	len++;
	dfs(dep+1);
	len--;
	x-=a[1][dep];
	y-=a[2][dep];
	z-=a[3][dep];
	dfs(dep+1);
	return ;
}
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[1][i];
	for(int i=1;i<=n;i++)cin>>a[2][i];
	for(int i=1;i<=n;i++)cin>>a[3][i];
	dfs(1);
	cout<<(ans==0?-1:ans);
	return ;
}
signed main(){
	int t=1;
	while(t--)solve();
	return 0;
} 

法2:分三个国家赢的情况讨论

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N], c[N], t[N], n;
//t[i]计算差值
int get(int x[], int y[], int z[]) {
    for (int i = 1; i <= n; i ++) {
        t[i] = x[i] - y[i] - z[i];
    }
    sort(t + 1, t + 1 + n, greater<>());//大的在左边
    for (int i = 1; i <= n; i ++) {
        t[i] += t[i - 1];//加上这个数
        if (t[i] <= 0) return i - 1;//如果和小于等于0的话那么就不行了 返回上一个最后>0的长度 i-1
    }
    return n;//一直没有返回那么全部都符合条件
}
signed main () {
    cin >> n;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    for (int i = 1; i <= n; i ++) cin >> b[i];
    for (int i = 1; i <= n; i ++) cin >> c[i];
    int res = max({get(a, b, c), get(b, a, c), get(c, a, b)});//max可以用于数组判最大
    cout << (res == 0 ? -1 : res) << '\n';
    return 0;
}

5395. 平均 - AcWing题库

//解法:排序加贪心
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define x first
#define y second
typedef pair<int, int> PII;
const int N=1e5+7;
int n;
vector<int>v[10];
int num[10];//记录每个数出现的次数 
void solve(){
	cin>>n; 
	int sur=n/10;
	for(int i=1;i<=n;i++){
		int a,b;cin>>a>>b;
		v[a].push_back(b);
		num[a]++; 
	}
	int ans=0;
	for(int i=0;i<=9;i++)sort(v[i].begin(),v[i].end());
	for(int i=0;i<=9;i++){
		if(num[i]>sur){
			int temp=num[i]-sur;//多出来temp个数需要改
			ans+=accumulate(v[i].begin(),v[i].begin()+temp,0);//注意后面的这个位置到不了  0-(temp-1)这是temp个数 
		}
	}
	cout<<ans;
	return ;
}
signed main(){
	int t=1;
	while(t--)solve();
	return 0;
} 

 4967. 翻转 - AcWing题库

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define x first
#define y second
typedef pair<int, int> PII;
const int N=1e5+7;
int n;
vector<int>v[10];
int num[10];//记录每个数出现的次数 
char change(char ch){
	if(ch=='1')return '0';
	return 1;
}
void solve(){
	string t,s;cin>>t>>s;//把s变成t 
	int len=t.size();
	if(s[0]!=t[0]||s[len-1]!=t[len-1]){
		cout<<-1<<'\n';
		return ;
	}//特判一下
	int cnt=0;
	for(int i=1;i<len-1;i++){
		if(s[i]!=t[i]){
			if(s[i-1]==s[i+1]&&s[i-1]!=s[i]&&s[i-1]==t[i]){
				cnt++;
				s[i]=change(t[i]);
			}else{
				cout<<-1<<'\n';
				return ;
			}
		}
	}
	cout<<cnt<<'\n';
	return ;
}
signed main(){
	int tt=1;cin>>tt;
	while(tt--)solve();
	return 0;
} 

 154. 滑动窗口 - AcWing题库

#include <iostream>
using namespace std;
const int N = 1000010;
int a[N], q[N], hh=1, tt = 0;
//q[i]用来存下标
//一般hh设在第一个元素 tt设在hh的前面一位
void getMinWindow(int n, int k) {
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        //右端点是i 长度是k 左端点是i-k+1>q[hh]的时候
        while(tt>=hh&&i-k+1>q[hh])hh++;//队首元素出队
        while (hh <= tt && a[i] <= a[q[tt]]) --tt;//维护队首元素是最小值
        q[++tt] = i;
        if (i >= k) cout << a[q[hh]] << " ";//形成了一个窗口
    }
    cout << '\n';
}

void getMaxWindow(int n, int k) {
    hh = 1; tt = 0;
    for (int i = 1; i <= n; ++i) {
        while(tt>=hh&&i-k+1>q[hh])hh++;
        while (hh <= tt && a[i] >= a[q[tt]]) --tt;
        q[++tt] = i;
        if (i >= k) cout << a[q[hh]] << " ";
    }
}

int main() {
    int n, k;
    cin >> n >> k;
    getMinWindow(n, k);
    getMaxWindow(n, k);
    return 0;
}

 4964. 子矩阵 - AcWing题库

#include<iostream>
#include<cstring>
using namespace std;
const int N=1010,mod=998244353;
int g[N][N],min1[N][N],min2[N][N],max1[N][N],max2[N][N];
int n,m,a,b;

void getmin1(int id){
    int q[N],tt=-1,hh=0;
    for(int i=1;i<=m;i++){
        while(tt>=hh&&i-k+1>q[hh])hh++;//队首元素的下标<窗口左端点时候
        while(tt>=hh&&g[id][q[tt]]>=g[id][i])tt--;
        q[++tt]=i;//维护每一行的最小值
        if(i>=b)min1[id][i]=g[id][q[hh]];//形成了一个窗口
    }
}

void getmin2(int id){
   int q[N],tt=-1,hh=0;
    for(int i=1;i<=n;i++){
        while(tt>=hh&&i-q[hh]+1>a)hh++;
        while(tt>=hh&&min1[q[tt]][id]>=min1[i][id])tt--;
        q[++tt]=i;//对于min1数组维护每一列的最小值 就得到了这个区间的最小值min1[i][j]就是以第i行第j列为右端点时候的最小值
        if(i>=a)min2[i][id]=min1[q[hh]][id];

    }
}

void getmax1(int id){
     int q[N],tt=-1,hh=0;
    for(int i=1;i<=m;i++){
        while(tt>=hh&&i-q[hh]+1>b)hh++;
        while(tt>=hh&&g[id][q[tt]]<=g[id][i])tt--;
        q[++tt]=i;
        if(i>=b)max1[id][i]=g[id][q[hh]];

    }
}

void getmax2(int id){
     int q[N],tt=-1,hh=0;
    for(int i=1;i<=n;i++){
        while(tt>=hh&&i-q[hh]+1>a)hh++;//超过了要出队
        while(tt>=hh&&max1[q[tt]][id]<=max1[i][id])tt--;
        q[++tt]=i;
        if(i>=a)max2[i][id]=max1[q[hh]][id];

    }
}


int main(){
    scanf("%d%d%d%d",&n,&m,&a,&b);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)scanf("%d",&g[i][j]);

    for(int i=1;i<=n;i++) getmin1(i);
    for(int i=b;i<=m;i++)getmin2(i);
    for(int i=1;i<=n;i++)getmax1(i);
    for(int i=b;i<=m;i++)getmax2(i);

    long long res(0);

    for(int i=a;i<=n;i++)
      for(int j=b;j<=m;j++) res+=((long long)min2[i][j]*max2[i][j])%mod,res%=mod;
//不断队mod取模
    printf("%d\n",res);
    return 0;
}

5021. 阶乘的和 - AcWing题库

//本题需要找到合并阶乘的规律
//如果题目中没有A[i]阶乘加和的条件,那么m就是数组A中的最小值
//而如果要对A[i]的阶乘加和,可能出现两个较小的数的阶乘合并成一个较大数的阶乘的现象
//如3个2的阶乘可以合并成3的阶乘,4个3的阶乘可以合并成4的阶乘
//故得到规律:若数字num出现了cnt次,且num可以被(cnt+1)整除,那么就可以合并成(num+1)的阶乘
//如样例中的2 2 2,可以将其合并成一个3,转化为数字3出现了一次 
//用map容器存储<数字,出现的次数>,再逐个数字验证是否满足如上规律,
//若满足则合并,并更新其出现的次数,若有不满足的数字则直接输出即可 
//元素个数不能超过1e5那么 最大可以被凑出来的阶乘就是100000!
//若所有数都大于1e5那么能整除的就只能是其中最小的 
//对于x>=k 则x!一定可以被k!整除 反之则不行 
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
map<ll,int>mp;//这个数字和这个数字出现的次数 
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        ll tmp;scanf("%lld",&tmp);
        mp[tmp]++;//用map记录每个数字出现的次数 
    }    
    map<ll,int>::iterator it;
    for(it=mp.begin();it!=mp.end();it++){//自动按照元素从小到大的顺序遍历 
        ll num=it->first;//取出当前元素 
        int cnt=it->second;//取出当前元素出现的次数 
        if(cnt%(num+1)==0){//满足阶乘合并的条件 
            mp[num+1]+=cnt/(num+1);//例如3个2的阶乘合并成1个3的阶乘,3的出现次数加1   num+1出现的次数加上cnt/(num+1)
        }
        else{
            printf("%lld\n",num);//否则到此为止,不能再合并阶乘,当前(最小)元素就是要找的m 
            return 0;
        }
    }
    return 0;
}

 5391. 奇怪的数 - AcWing题库

法1:爆搜
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
const int mod=998244353;
int aa[N];
int dp[N][46];
int ans=0;
int n,m;
void dfs(int dep,int a,int b,int c,int d,int e){//表示现在正在看第dep位
    if(dep==n+1){
      if(a+b+c+d+e<=m)ans=(ans+1)%mod;//这句要放在里面
        return ;
    } 
    if(a+b+c+d+e>m)return ;
    for(int i=aa[dep];i<10;i+=2){
        dfs(dep+1,b,c,d,e,i);
    }
}
signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)aa[i]=(i%2==1);//奇数位置都设为1
    
    for(int i=aa[1];i<10;i+=2){
        for(int j=aa[2];j<10;j+=2){
            for(int h=aa[3];h<10;h+=2){
                for(int k=aa[4];k<10;k+=2){
                  for(int l=aa[5];l<10;l+=2){
                    dfs(6,i,j,k,h,l); 
                  }
                }
            }
        }
    }
    cout<<ans;
    return 0;
}

4971. 子树的大小 - AcWing题库

//对于结点i,本题如果能找到i的最左孩子和最右孩子的下标,便可以迎刃而解
//对于第i个结点,其前面有i-1个结点,每个结点各有m个孩子,再加上1号结点+1就是左孩子 
//可得第i个结点的最左孩子下标为(i-1)*m+2
//同理可得第i个结点的最右孩子下标为i*m+1(前i个结点各有m个孩子,再加上1号结点)就是右孩子 
//故对于子树根结点,只需逐层累加最右孩子-最左孩子即可 
//只需要处理最后一层的特殊情况:
//1、最左孩子下标超出n,出说明子树在最后一层没有结点,直接退
//2、最右孩子下标超出n,说明子树在最后一层的结点是非满的,将最右孩子下标改为n,累加后退出
//有1e5组询问,对于每组询问,可在近似logm n的时间复杂度内得出结果,不会超时 
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int N=1e5+10;
const int mod=998244353;
int aa[N];
int dp[N][46];
int ans=0;
//第i层有m^(i-1)个结点  从(m^(i-1)+1) /2开始 
void solve(){
	int n,m,k;cin>>n>>m>>k;
	int cnt=1;
	int lchild=k;
	int rchild=k;
	while(1){
		bool flag=0;
		lchild=(lchild-1)*m+2;
		rchild=rchild*m+1;
		if(lchild>n)break;//左孩子超出直接跳 
		if(rchild>=n){//最右孩子超出n 
			flag=1;//这是最后一次
			 rchild=n;//改为n 
		}
		cnt+=rchild-lchild+1;
		if(flag)break;
	} 
	cout<<cnt;
	return ;
}
signed main(){
    int t=1;cin>>t;
    while(t--)solve();
    return 0;
}

 

5394. 反异或01串 - AcWing题库

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值