后天就是蓝桥杯省赛了,今天总结一下这段时间做的蓝桥杯历届试题,还是一个一个题目的来吧!!!!!!
1,历届试题 矩阵翻硬币
这个题目说真的,我不会,在网上看了某神牛的题解答案为 ans=sqrt(n)*sqrt(m),具体怎么证明的我也不知道
2,历届试题 兰顿蚂蚁
#include<bits/stdc++.h> using namespace std; #define maxn 100+10 int a[2][maxn],n; bool ok(int cur) { for (int i=2;i<=n;i++) if (a[cur][i]!=a[cur][i-1]) return 0; return 1; } int main() { while(scanf("%d",&n)!=EOF) { int cur=0,ans=0; for (int i=1;i<=n;i++) scanf("%d",&a[cur][i]); while(!ok(cur)) { for (int i=1;i<=n;i++) { if (a[cur][i]%2) { a[cur][i]++; ans++; } } for (int i=1;i<n;i++) a[1-cur][i]=a[cur][i+1]/2; a[1-cur][n]=a[cur][1]/2; for (int i=1;i<=n;i++) a[1-cur][i]+=(a[cur][i]/2); cur=1-cur; } printf("%d ",ans); } return 0; }
4,历届试题 小朋友排队
这个题让我想起了去年武大校赛的一道题,我永远忘不了,求最小交换次数就是求逆序对数,而这个题要算出每个小朋友交换的次数,故我们从前面求一次,再从后面求一次就可以了,我这里用树状数组来求
#include<bits/stdc++.h> using namespace std; #define maxn 1000000+10 #define LL long long int a[maxn],n,b[maxn],num[maxn],c[maxn]; LL sum[maxn]; void init() { sum[0]=0; for (LL i=1;i<maxn;i++) sum[i]=sum[i-1]+i; } int lowbit(int x){ return x&(-x); } void add(int x) { while(x<maxn) { a[x]++; x+=lowbit(x); } } int get_sum(int x) { int ans=0; while(x>0) { ans+=a[x]; x-=lowbit(x); } return ans; } int main() { init(); while(scanf("%d",&n)!=EOF) { memset(a,0,sizeof(a)); memset(num,0,sizeof(num)); for (int i=1;i<=n;i++) scanf("%d",&b[i]),c[i]=b[i]; sort(c+1,c+n+1); int m=unique(c+1,c+n+1)-c; for (int i=1;i<=n;i++) b[i]=lower_bound(c+1,c+m+1,b[i])-c; for (int i=1;i<=n;i++) { num[i]+=get_sum(maxn-1)-get_sum(b[i]); add(b[i]); } memset(a,0,sizeof(a)); for (int i=n;i>=1;i--) { num[i]+=get_sum(b[i]-1); add(b[i]); } LL ans=0; //for (int i=1;i<=n;i++) printf("%d ",num[i]); printf(" "); for (int i=1;i<=n;i++) ans+=sum[num[i]]; printf("%I64d ",ans); } return 0; }
5,历届试题 波动数列
这个题目一看,就知道是枚举a出现的次数和b出现的次数,网上有人用BFS和DFS来做,想都不想,肯定超时!!!!!!可以设第一个数为x,则
S=n*x+sgm i*(a or -b),则知道a和b的数目为n*(n-1)/2,则可以枚举a的数目A,知道A后,就看组成A有多少种方法,而我们只能用1,2,。。。。n-1来组成A,这不就是01背包吗?这样题目就不能了,最坏情况下时间复杂度为O(n*n^2)=n^3,但是可以的70分了,正确的解法不知道
#include<bits/stdc++.h> using namespace std; #define LL long long #define maxn 2000+100 const int mod=100000007; LL dp[2000000]; int a[maxn]; void init(int n) { for (int i=1;i<=n;i++) a[i]=i; int N=(n+1)*(n+2)/2; memset(dp,0,sizeof(dp)); dp[0]=1; for (int i=1;i<=n;i++) { for (int j=N-a[i];j>=0;j--) { if (dp[j]) dp[j+a[i]]=(dp[j+a[i]]+dp[j])%mod; } } } int main() { LL n,s,a,b; while(scanf("%I64d %I64d %I64d %I64d",&n,&s,&a,&b)!=EOF) { init(n-1); LL ans=0; LL N=n*(n-1)/2; for (LL i=0;i<=N;i++) { LL j=N-i; LL t=s-i*a+j*b; if (t%n==0) ans=(ans+dp[i])%mod; } printf("%I64d ",ans); } return 0; }
6,历届试题 地宫取宝
这个题目说真的,就是一个裸的DP,DP[i][j][k]表示现在在(i,j)这个位置而且拿了这个数,手上有k个数的情况,然后进行转移,记得乘一个组合数就可以了,因为即使路径相同,取得数的位置不同也算一种取法
#include<bits/stdc++.h> using namespace std; #define maxn 50+10 #define LL long long const int mod=1000000007; int a[maxn][maxn]; LL dp[maxn][maxn][20],C[maxn][maxn]; void init() { C[0][0]=1; for (int i=1;i<=50;i++) { C[i][0]=1; C[i][i]=1; for (int j=1;j<i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod; } } int main() { int n,m,K; init(); while(scanf("%d%d%d",&n,&m,&K)!=EOF) { for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&a[i][j]); memset(dp,0,sizeof(dp)); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { LL tp=C[i-1+j-1][i-1]; dp[i][j][1]=tp; } for (int i=1;i<=n;i++) { for (int j=1;j<=m;j++) { for (int k=1;k<=K;k++) { for (int ii=1;ii<=i;ii++) { for (int jj=1;jj<=j;jj++) { if (a[i][j]<=a[ii][jj]) continue; LL tp=C[i-ii+j-jj][i-ii]; dp[i][j][k]=(dp[i][j][k]+(dp[ii][jj][k-1]*tp)%mod)%mod; } } } } } LL ans=0; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { LL tp=C[n-i+m-j][n-i]; ans=(ans+(dp[i][j][K]*tp)%mod)%mod; } printf("%I64d ",ans); } return 0; }
7,历届试题 蚂蚁感冒
感觉这个题有点盗刘汝佳了,在《算法入门经典训练指南》里面第一章就有这个类似的题目,就把蚂蚁碰面掉头堪称穿过就可以了
#include<bits/stdc++.h> using namespace std; #define maxn 100+10 int a[maxn]; int main() { int n,ans; while(scanf("%d",&n)!=EOF) { scanf("%d",&a[1]); int L=0, R=0; for (int i=2;i<=n;i++) { scanf("%d",&a[i]); if (a[i]<0 && abs(a[i])>abs(a[1])) R++; if (a[i]>0 && abs(a[i])<abs(a[1])) L++; } if ((a[1]<0 && L==0) || (a[1]>0 && R==0)) ans=1; else ans=1+L+R; printf("%d ",ans); } return 0; }
8,历届试题 最大子阵
这个没有什么好说的了,n^4优化成n^3,类似于最大连续和的线性时间算法
#include<bits/stdc++.h> using namespace std; #define maxn 500+10 #define inf 0x3f3f3f3f #define LL long long LL a[maxn][maxn],sum[maxn][maxn],b[maxn]; int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%I64d",&a[i][j]); memset(sum,0,sizeof(sum)); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) sum[i][j]=sum[i-1][j]+a[i][j]; LL ans=-9999999999999999; b[0]=0; for (int up=1;up<=n;up++){ for (int down=up;down<=n;down++){ for (int i=1;i<=m;i++) b[i]=b[i-1]+sum[down][i]-sum[up-1][i]; LL minv=0; for (int i=1;i<=m;i++) { ans=max(ans,b[i]-minv); minv=min(minv,b[i]); } } } printf("%I64d ",ans); } return 0; }
9,历届试题 城市建设
这个题搞不懂怎么处理,n^2建图,跑最小生成树可以得到70fen
10,历届试题 邮局
这个题目感觉好像很老了,直接DFS加剪枝,不管你怎么剪枝,我觉得得分都不会很差
10,历届试题 国王的烦恼
这个题一开始没有什么思路,后来想想不就是支持一个删边后快速判断两个点是不是属于同一个集合,删边我们没有办法,但是我们可以从后面来,变成加边,这样并查集就可以了,注意会有很多时间是相同的,要一段一段的处理
#include<bits/stdc++.h> using namespace std; #define maxn 100000+10 int f[maxn],n; struct node{ int u,v,t; node(){} node(int u,int v,int t):u(u),v(v),t(t){} bool operator < (const node &A)const{ return t>A.t; } }P[maxn*10]; void init(){ for (int i=0;i<=n;i++) f[i]=i; } int find(int x){ return x==f[x]? x:f[x]=find(f[x]); } int main() { int m; while(scanf("%d%d",&n,&m)!=EOF) { init(); for (int i=1;i<=m;i++) scanf("%d%d%d",&P[i].u,&P[i].v,&P[i].t); sort(P+1,P+m+1); P[m+1].t=-1; int id=1; int ans=0; while(id<=m) { //cout<<"BUGBUGBUG"<<endl; int j=id; while(P[j].t==P[id].t)j++; bool flag=0; for (int i=id;i<j;i++) { int x=find(P[i].u); int y=find(P[i].v); if (x!=y)flag=1; } if (flag) ans++; for (int i=id;i<j;i++) { int x=find(P[i].u); int y=find(P[i].v); f[x]=y; } id=j; } printf("%d ",ans); } return 0; }
11,历届试题 回文数字
这个题更是一道彻彻底底的送分题,直接暴力处理5位到6位的数就可以
#include<bits/stdc++.h> using namespace std; vector<int>G; bool ok(int i,int n){ int a[10],u=0,ret=0; while(i) { a[++u]=i%10; i=i/10; ret+=a[u]; } if (ret!=n) return 0; for (int i=1;i<=u;i++) if (a[i]!=a[u-i+1]) return 0; return 1; } void work(int n){ G.clear(); for (int i=10000;i<=999999;i++) { if (ok(i,n)) G.push_back(i); } } void output() { int len=G.size(); if (!len) { printf("-1 "); return; } for (int i=0;i<len;i++) printf("%d ",G[i]); } int main(){ int n; while(scanf("%d",&n)!=EOF) { work(n); output(); } return 0; }
12,历届试题 九宫重排
八数码的变形题目,本以为要hash一下的,想不到一个map就可以了,这个题不多说了,直接BFS就可以,注意矩阵和数组下标的转换
#include<bits/stdc++.h> using namespace std; map<string,int>G; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; struct node{ char s[10]; int d,u; node(){} node(char *S,int D,int U){ strcpy(s,S); d=D; u=U; } }; bool ok(int x,int y) { return (x>=0 && x<=2 && y>=0 && y<=2); } int bfs(char *s,char *t) { int id; for (int i=0;i<strlen(s);i++) if (s[i]=='.') id=i; queue<node>q; G.clear(); G[s]=1; while(!q.empty()) q.pop(); q.push(node(s,0,id)); while(!q.empty()) { node tp=q.front(); q.pop(); if (strcmp(tp.s,t)==0) return tp.d; int x=tp.u/3, y=tp.u%3; for (int i=0;i<4;i++) { int tpx=x+dx[i], tpy=y+dy[i]; if (ok(tpx,tpy)) { int u=tpx*3+tpy; char c[10]; strcpy(c,tp.s); swap(c[tp.u],c[u]); if (G.find(c)==G.end()) { G[c]=1; q.push(node(c,tp.d+1,u)); } } } } return -1; } char s[10],t[10]; int main(){ while(scanf("%s%s",s,t)!=EOF) { int ans=bfs(s,t); printf("%d ",ans); } return 0; }
13,历届试题 危险系数
这个题说是割点,其实就是一个DFS题目,一个点一个点的判断,去掉一个点后DFS一遍,看看S和T是否连通就可以
#include <bits/stdc++.h> using namespace std; #define maxn 2000+10 vector<int>G[maxn]; bool vis[maxn]; int s,t; int dfs(int u) { for (int i=0;i<G[u].size();i++) { int v=G[u][i]; if (!vis[v]) { vis[v]=1; dfs(v); } } } int main() { int n,m,x,y; while(scanf("%d%d",&n,&m)!=EOF) { for (int i=0;i<=n;i++) G[i].clear(); for (int i=1;i<=m;i++) { scanf("%d%d",&x,&y); G[x].push_back(y); G[y].push_back(x); } scanf("%d%d",&s,&t); int ans=0; for (int i=1;i<=n;i++) { if (i==s || i==t) continue; memset(vis,0,sizeof(vis)); vis[s]=vis[i]=1; dfs(s); if (!vis[t]) ans++; } printf("%d ",ans); } return 0; }
14,历届试题 幸运数
更上面分糖果那个题一样,用两个数组来模拟一下就行
#include <bits/stdc++.h> #define inf 0x3f3f3f3f #define PI acos(-1.0) #define eps 1e-8 #define LL long long #define MEM(a,b) memset(a,b,sizeof(a)) #define PB push_back #define MP make_pair #define PQ priority_queue #define MII map<int,int>::iterator #define MLL map<LL,LL>::iterator #define pii pair<int,int> #define SI set<int>::iterator #define SL set<LL>::iterator #define MSI map<string,int>::iterator #define M_SI multiset<int>::iterator #define IN freopen("in.txt","r",stdin); #define OUT freopen("out.txt","w",stdout); #define BUG printf("bug************bug************bug "); using namespace std; #define maxn 1000000+10 vector<int>q[2]; int a[maxn]; void init(int N) { q[0].clear(); q[1].clear(); for (int i=1;i<N;i+=2) q[0].PB(i); int cur=0; for (int i=1;i<N;i++) { q[1-cur].clear(); int len=q[cur].size(); if (len<i+1) break; int u=q[cur][i]; if (u>len) break; for (int j=0;j<len;j++) { if ((j+1)%u!=0) q[1-cur].PB(q[cur][j]); } cur=1-cur; } for (int i=0;i<q[cur].size();i++) a[q[cur][i]]=1; for (int i=0;i<=N;i++) a[i]+=a[i-1]; } int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { init(max(n,m)); printf("%d ",a[m-1]-a[n]); } return 0; }
15,历届试题 大臣的旅费
这个题就是求一棵树的直径,DFS两遍就可以,在这篇文章的上一篇就说了这道题
#include <bits/stdc++.h> #define inf 0x3f3f3f3f #define PI acos(-1.0) #define eps 1e-8 #define LL long long #define MEM(a,b) memset(a,b,sizeof(a)) #define PB push_back #define MP make_pair #define PQ priority_queue #define MII map<int,int>::iterator #define MLL map<LL,LL>::iterator #define pii pair<int,int> #define SI set<int>::iterator #define SL set<LL>::iterator #define MSI map<string,int>::iterator #define M_SI multiset<int>::iterator #define IN freopen("in.txt","r",stdin); #define OUT freopen("out.txt","w",stdout); #define BUG printf("bug************bug************bug "); using namespace std; #define maxn 100000+10 struct node { int id,cost; node(){} node(int id,int cost):id(id),cost(cost){} }; vector<node>G[maxn]; LL ans[maxn]; void init() { ans[0]=0; for (int i=1;i<maxn;i++) {G[i].clear();ans[i]=ans[i-1]+i+10; } } priority_queue<pii,vector<pii>,greater<pii> >q; int d[maxn],n; bool vis[maxn]; void dijkstra(int k) { for(int i=0;i<=n;i++) d[i]=(i==k? 0 : inf); memset(vis,0,sizeof(vis)); q.push(make_pair(d[k],k)); while(!q.empty()) { pii u=q.top(); q.pop(); int x=u.second; if(!vis[x]) { vis[x]=true; for (int i=0;i<G[x].size();i++) { if(d[G[x][i].id]>d[x]+G[x][i].cost) { d[G[x][i].id]=d[x]+G[x][i].cost; q.push(make_pair(d[G[x][i].id],G[x][i].id)); } } } } } int main() { int n,x,y,c; while(scanf("%d",&n)!=EOF) { init(); for (int i=1;i<=n;i++) d[i]=inf; for (int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&c); G[x].PB(node(y,c)); G[y].PB(node(x,c)); } d[1]=0; dijkstra(1); int dist=0,u; for (int i=1;i<=n;i++) if (d[i]>dist) dist=d[i],u=i; for (int i=1;i<=n;i++) d[i]=inf; d[u]=0; dijkstra(u); dist=0; for (int i=1;i<=n;i++) dist=max(dist,d[i]); printf("%I64d ",ans[dist]); } return 0; }
16, 历届试题 买不到的数目
可以知道,如果a为小的那个,那么如果x,x+1,x+2.。。。。。x+a-1可以得到,那么比x大的就可以得到了,然后就是一个有两种物品的完全背包了(背包真是强大)
#include <bits/stdc++.h> using namespace std; #define maxn 2000000+10 int dp[maxn],a[3]; int main() { while(scanf("%d%d",&a[1],&a[2])!=EOF) { if (a[1]>a[2]) swap(a[1],a[2]); memset(dp,0,sizeof(dp)); dp[0]=1; for (int i=1;i<=2;i++) { for (int j=a[i];j<maxn;j++) if (dp[j-a[i]]) dp[j]=1; } dp[0]=0; for (int i=1;i<maxn;i++) dp[i]+=dp[i-1]; int ans=0; for (int i=1;i+a[1]<maxn;i++) { int x=i-1; int y=i+a[1]-1; if (dp[y]-dp[x]==a[1]) { ans=i; break; } } printf("%d ",ans-1); } return 0; }
在后面的那些题目感觉没有什么难度,也没有什么好总结的,这里就不再说了
作者 chensunrise