A: 組隊
輸入數據
編號 號位1 2 3 4 5
1 97 90 0 0 0
2 92 85 96 0 0
3 0 0 0 0 93
4 0 0 0 80 86
5 89 83 97 0 0
6 82 86 0 0 0
7 0 0 0 87 90
8 0 97 96 0 0
9 0 0 89 0 0
10 95 99 0 0 0
11 0 0 96 97 0
12 0 0 0 93 98
13 94 91 0 0 0
14 0 83 87 0 0
15 0 0 98 97 98
16 0 0 0 93 86
17 98 83 99 98 81
18 93 87 92 96 98
19 0 0 0 89 92
20 0 99 96 95 81
這題直接暴力就完事了,時間復雜度是20的5次方3.2E6,完全不會超時。
#include<iostream>
#include<cstring>
#include<algorithm>
int visit[30],ans;
using namespace std;
struct people {
int score[6];
}a[30];//存入同一個人的不同位置分數
void dfs(int pos,int sum) {//sum是當前枚舉分數,pos是當前枚舉的位置。
if(pos==6) {
ans=max(ans,sum);
return ;
}
for(int i=1;i<=20;i ) {
if(!visit[i]) {
visit[i]=1;
dfs(pos 1,sum a[i].score[pos]);
visit[i]=0;
}
}
}
int main()
{
freopen("D:\\MY\\ce.txt","r",stdin);
int t;
ans=0;
for(int i=1;i<=20;i ) {
visit[i]=0;
cin>>t;
for(int j=1;j<=5;j )
cin>>a[i].score[j];//這個人在第i位的分數。
}
dfs(1,0);
cout<<ans<<endl;
return 0;
}
最后答案是490.
B: 年號字串
這題是普通進制的轉換,沒什么特別。
但是這里最重要的就是對於余數是零的要進行處理,將其轉換為26,也就是Z。
#include<iostream>
char a[30],ans[30];
using namespace std;
void init() {
for(int i=0;i<26;i )
a[i 1]=(char)('A' i);
}
int main()
{
init();//初始化數組a,為A~Z。
int n;
while(cin>>n) {
int pos=0;
while(n) {
int t=n%26;
if(t==0) {//這一步中,對於余數是零的要轉化為26,
t=26;//所以n要減去26,t要是26。
n-=26;
}
ans[pos ]=a[t];
n/=26;
}
pos--;
for(int i=pos;i>=0;i--)
cout<<ans[i];
cout<<endl;
}
return 0;
}
答案是BYQ.
C: 數列求值
簡單的遞歸,遞歸的時候磨去10000,保留最后4位數字,
但是這里的第20190324項可能過於大,單純的遞歸可能出不來結果。
~可以寫個記憶化。
~或者寫個for遞推跑出來。
~再高級一點就是矩陣快速冪了。
一、遞推寫法
#include<iostream>
#define mood 10000
const int maxn=3e7;
int a[maxn]={0,1,1,1};
using namespace std;
int main()
{
for(int i=4;i<=20190324;i )
a[i]=(a[i-1] a[i-2] a[i-3])%mood;
cout<<a[20190324];
return 0;
}
二、記憶化遞歸
#include<iostream>
#define mood 10000
const int maxn=3e7;
int a[maxn]={0,1,1,1};
using namespace std;
int dfs(int s) {
if(a[s]) return a[s];
a[s]=(dfs(s-1) dfs(s-2) dfs(s-3))%mood;
return a[s];
}
int main()
{
int n=20190324;
printf("%d\n",dfs(n));
return 0;
}
這個試過了,跑不出來,到十萬多的時候就不行了
三、矩陣快速冪
#include<iostream>
#define mood 10000
typedef long long ll;
const int N=3;
using namespace std;
struct unit {
ll a[N][N];
};
unit the(unit a,unit b) {
unit temp;
for(int i=0;i<N;i )
for(int j=0;j<N;j ) {
temp.a[i][j]=0;
for(int k=0;k<N;k )
temp.a[i][j]=(temp.a[i][j] a.a[i][k]*b.a[k][j])%mood;
}
return temp;
}
int main() {
int n;
while(cin>>n) {
if(n<=3) {
cout<<1<<endl;
continue;
}
unit ans={
1,1,1,
0,0,0,
0,0,0
};
unit temp= {
1,1,0,
1,0,1,
1,0,0
};
n-=3;
while(n) {
if(n&1) ans=the(ans,temp);
temp=the(temp,temp);
n>>=1;
}
cout<<ans.a[0][0]<<endl;
}
return 0;
}
答案是4659.
D: 數的分解
#include<iostream>
int ans[5]={1},sum;
using namespace std;
bool judge(int x) {
while(x) {
int t=x%10;
if(t==2||t==4)
return true;
x/=10;
}
return false;
}
int main()
{
sum=0;
int n=2019;
for(int i=1;i<=n;i ) {
if(judge(i))
continue;
for(int j=i 1;j<=n;j ) {
if(i j>=n)
break;
if(judge(j)||i==j)
continue;
for(int k=j 1;k<=n;k ) {
if(i j k>n)
break;
if(judge(k)||k==i||j==k)
continue;
if(i j k==n)
sum ;
}
}
}
cout<<sum<<endl;
return 0;
}
直接暴力,一秒之內出結果,但是要注意,i<j<k這樣才能保證,不會出現重復的情況,
這里的剪枝除了有 i<j<k,
還要注意當i j>=n || i j k>n的時候要直接break出去。
本來想用dfs的遞歸的,但是一直剪枝出問題,出不來正確答案,就用這個for循環吧,
答案40785.
E: 迷宮
這個看參考我的上一篇博客,2019藍橋杯E題迷宮
F: 特別數的和
#include<iostream>
using namespace std;
bool judge(int x) {
while(x) {
int t=x%10;
if(t==2||t==0||t==1||t==9)
return true;
x/=10;
}
return false;
}
int main()
{
int n;
while(cin>>n) {
int sum=0;
for(int i=1;i<=n;i )
if(judge(i))
sum =i;
cout<<sum<<endl;
}
return 0;
}
一個循環加一個judge函數判斷數字是否含有2,0,1,9,
直接暴力,最大10000的測試樣例也是秒出,感覺比前面的題都更水。
G:完全二叉樹的權值
只要判斷當前值的數組下標是再哪一深度下就行了,第i層深度共有
2(i-1)個數,這里的最大N<=100000,而217是131072,因此最多有17 1=18層
#include<iostream>
#include<cstring>
#include<cmath>
typedef long long ll;
ll a[20];//存放第i-1層的權值。
using namespace std;
int main()
{
int n;
while(cin>>n) {
memset(a,0,sizeof(a));
int l;
for(int i=0;;i )//尋找最大深度層。
if(n<=pow(2,i)) {
l=i;
break;
}
int pos=0,flag;
ll ans=0;
for(int i=0;i<=l;i ) {
int longs=pow(2,i);
for(int j=0;j<longs&&pos <n;j ){//讀入的操作,定義一個
ll temp; //pos來保證讀入的數字個數
cin>>temp; //與n是一樣的。
a[i] =temp;
}
if(a[i]>ans) {
ans=a[i];
flag=i 1;//i是深度-1,所以真正深度應該是i 1。
}
}
cout<<flag<<endl;
}
return 0;
}
H:等差數列
#include<iostream>
#include<cmath>
#include<algorithm>
const int maxn=100010;
int a[maxn];
using namespace std;
int gcd(int x,int y) {
if(y==0) return x;
return gcd(y,x%y);
}
int main()
{
int n;
while(cin>>n) {
for(int i=0;i<n;i )
cin>>a[i];
sort(a,a n);
int mina=a[0],maxa=a[n-1];
for(int i=1;i<n;i )
a[i-1]=a[i]-a[i-1];
sort(a,a n-1);
if(a[0]==0) {
cout<<n<<endl;
continue;
}
int step=a[0];
for(int i=1;i<n-1;i ) {
if(a[i]%step==0)
continue;
step=gcd(step,a[i]);
}
cout<<(maxa-mina)/step 1<<endl;
}
return 0;
}
思路就是排序找對大的數和最小的數,然后求得有序數的兩數之間的差,然后再找這些差之間的最大公因數,
最后對求得的公因數進行分類,
為零時,就是常數列,所以長度時n
不為零時,長度是(最大值減最小值)/所得的公因數 1。
寫了這一題,我真正明白了暴力杯這個稱號,我都這么折騰了,他還是不超時,以后有什么題不能優化,直接暴力就完事了。
I:后綴表達式.
這道題就是有點難搞清楚n,m之間的關系,
假設m為零,答案很簡單了,就是n 1個數相加。
當m不為零時,我們可以通過括號把所有的加號變成減號。所以這道題目就變成了,都是加號,或者都是減號的問題,都是加號的問題上面已近考慮完了,
下面我們來考慮都是減號的問題,假設符號數時n m=L減號我們又可以通過加括號的方式,減去1~~L個數,最少減去一個數,最多減去L個數,
於是這里有兩種特殊情況:
全是負數,這里我們應當選取最大的負數不添加減號。
全是正數,這里我們應但選取最小的負數添加減號。
#include<iostream>
typedef long long ll;
const int maxn=2e5 10;
ll a[maxn];
using namespace std;
int main()
{
int n,m,l;
cin>>n>>m;
l=n m 1;
ll minzheng=2000000000,maxfu=-2000000000;
int zheng=0,fu=0;
for(int i=0;i<l;i ) {
cin>>a[i];
if(a[i]>0) {
zheng ;
minzheng=min(minzheng,a[i]);
}
else if(a[i]<0) {
fu ;
maxfu=max(maxfu,a[i]);
}
}
ll sum=0;
if(m==0) {
for(int i=0;i<l;i )
sum =a[i];
cout<<sum<<endl;
}
else {
if(zheng==l) {
for(int i=0;i<l;i )
sum =a[i];
cout<<sum-2*minzheng<<endl;
}
else if(fu==l) {
for(int i=0;i<l;i )
sum-=a[i];
cout<<sum 2*maxfu<<endl;
}
else {
for(int i=0;i<l;i ) {
if(a[i]>0)
sum =a[i];
else
sum-=a[i];
}
cout<<sum<<endl;
}
}
return 0;
}
J: 靈能傳送.
菜雞的我看完這道題,就只會一個公式,( a[i-1] , a[i] , a[i-1] ) 轉變后變成了,
( a[i-1] a[i] , -a[i] , a[i 1] a[i] ),
然后想到要用前綴和或者差分,但還是不知道怎么寫。在B站看了yxc大佬的acwing視頻后,才明白如何寫
這道題大致分為兩種情況,在對情況進行討論前,我們設定一些必要的約定。
數組讀入從1開始,一直到n,我們假設sum數組,sum[i]表示的是第i項的前綴和,設定s0=0,對上述公式的變換就可以寫成
(sum[i-1] , sum[i] , sum[i 1])變成(sum[i] , sum[i-1] , sum[i 1])
這里不難發現,就是對下標從 1~(n-1)的前綴和數組進行交換,
—、第一種情況,如圖
這是最佳排列方式
假設最大的一項和最小的一項在第0個和第n個前綴和中。
要保證每兩項之間的差的絕對值最小,只有降序排列(最大在首,最小在尾。),或者升序排列(最小在首,最大在尾。)
二、第二種情況,如圖
這個是這種情況的最佳排列方式
最大和最小不一定都在首尾,先把選點排序,
這里我們的選點方式應該是跳躍式的選點方式,如果,有兩個選點相連,那么一定有,兩個選點的間距大於二,那么最大值一定會比這兩個相連的選點的值更大,
我們把因為第0個位置和第n個位置不能變,
我們把所有選點投射到y軸上,從數值為sum[0]的點開始向下,step=2的步長選點,從數值為sum[n]的點開始向上選點,step也是2,然后把剩下的點,按照升序補齊中間的一段。在對整個前綴和求出兩個之間絕對值最大的數。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
typedef long long ll;
const int maxn=300010;
ll sum[maxn],a[maxn];//sum是用存放排序前的前綴和數組,a是用來存放最后答案的前綴和數組。
int visit[maxn];
using namespace std;
int main()
{
// freopen("D:\\MY\\ce.txt","r",stdin);
int t;
cin>>t;
while(t--) {
int n;
cin>>n;
sum[0]=0;//初始化第一個前綴和為0.
for(int i=1;i<=n;i ) {
ll t;
cin>>t;
sum[i]=sum[i-1] t;//存放初始前綴和。
}
ll s0=sum[0],sn=sum[n];
if(s0>sn) swap(s0,sn);//交換順序,滿足s0<sn,便於運算,
sort(sum,sum n 1);//對前綴和數組排序,
s0=lower_bound(sum,sum n 1,s0)-sum;//找到s0位於sum數組種的位置。
sn=lower_bound(sum,sum n 1,sn)-sum;//找到sn位於sum數組中的位置。
int l=0,r=n;
memset(visit,0,sizeof(visit));
for(int i=s0;i>=0;i-=2) {//從s0開始向下找數。
a[l ]=sum[i];
visit[i]=1;
}
for(int i=sn;i<=n;i =2) {//從sn開始向上招數。
a[r--]=sum[i];
visit[i]=1;
}
for(int i=0;i<=n;i )//用剩下的數補齊a數組。
if(!visit[i])
a[l ]=sum[i];
ll ans=0;
for(int i=1;i<=n;i )
ans=max(ans,abs(a[i]-a[i-1]));//最后得到絕對值最大的數,
cout<<ans<<endl;
}
return 0;
}
終於寫完了這篇題解了。
心得:
除了后兩道題是我真不會的,但是前面的題在oj上提交我也不能保證一次就對。還有就是,我這次真正明白了什么是藍橋杯,“暴力杯”了,藍橋杯的題實在不能優化,就直接暴力完事了。