蓝桥杯B组C++历届真题代码整理
2013年省赛真题
高斯日记
注意4月30号是第一天,可以先通过给出的例子5343天来验证。
#include <iostream>
using namespace std;
void datesearch(int number, int nowyear, int nowmonth, int Month[])
{
number -= 1;
int ansyear = 1777, ansmonth = 4, ansday = 30;
for(int year = nowyear;; year++)
{
//cout << " year = " << year << " number = " << number << endl;
if(number == 0) break;
if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) Month[1] = 29;
else Month[1] = 28;
if(year != nowyear) nowmonth = 0;
for(int month = nowmonth; month < 12; month++)
{
if(Month[month] >= number)
{
ansyear = year;
ansmonth = month+1;
ansday = number;
number = 0;
break;
}
else
number -= Month[month];
}
}
cout << ansyear << "-" << ansmonth << "-" << ansday << endl;
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int number = 8113;
datesearch(number, 1777, 4, month);
return 0;
}
马虎的算式
直接枚举所有的值就行.
#include <iostream>
using namespace std;
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
int sum = 0;
for(int g = 1; g <= 9; g++) //g
{
for(int h = 1; h <= 9; h++) // h
{
if(h == g) continue;
for(int j = 1; j <= 9; j++) //j
{
if(j == g || j == h) continue;
for(int k = 1; k <= 9; k++) // k
{
if(k == g || k == h || k == j) continue;
for(int l = 1; l <= 9; l++) //l
{
if(l == g || l == h || l == j || l == k) continue;
int ab = g*10 + h;
int cde = j*100 + k*10 + l;
int adb = g*100 + k*10 + h;
int ce = j*10 + l;
if(ab * cde == adb * ce) sum ++;
}
}
}
}
}
cout << sum << endl;
return 0;
}
第39级台阶
注意刚开始处于第0级台阶而不是第1级。
解法1, 用bfs来做。
#include <iostream>
#include <queue>
using namespace std;
struct node
{
int footstep; // 脚的步数,用来判断奇偶性
int step; // 已经走了的台阶数
node(int ft,int s): footstep(ft), step(s){}
};
void bfs()
{
queue<node> Q;
Q.push(node(0, 0));
int sum = 0;
while(!Q.empty())
{
node N = Q.front();
Q.pop();
if(N.footstep % 2 == 0 && N.step == 39) sum++;
else
{
if(N.step + 1 <= 39) Q.push(node(N.footstep+1, N.step+1));
if(N.step + 2 <= 39) Q.push(node(N.footstep+1, N.step+2));
}
}
cout << sum << endl;
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
bfs();
return 0;
}
解法2,用dfs来做。利用递推式 f(n) = f(n-1) + f(n-2). 第N级台阶数一定是第N-1和N-2级台阶数的和,然后从第39级台阶开始递归。
#include <iostream>
#include <queue>
using namespace std;
int ans = 0;
void dfs(int n, int step)
{
if(n < 0) return ;
if(n == 0 && step % 2 == 0)
{
ans++;
return ;
}
dfs(n-1, step+1);
dfs(n-2, step+1);
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
dfs(39, 0);
cout << ans << endl;
return 0;
}
黄金连分数
这道题作为填空题来说是非常麻烦的。
首先找出这个分数的规律,从分数的分子分母来判断出是个斐波那契数列。然后精确到小数点后一百位的除法计算又是大数的计算。所以要实现斐波那契的大数加法和大数除法。最后还要精确到小数点后101位,因为第101要四舍五入。精确的小数点后100位需要多次的输出来比较,等到100位都不会变化时就是稳定的时候。
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn = 10;
string add(string a, string b)
{
a = a.substr(a.find_first_not_of('0'));
b = b.substr(b.find_first_not_of('0'));
long long lenA = a.length();
long long lenB = b.length();
long long len = max(lenA,lenB) + maxn;
int tmp = 0;
string ans(len, '0');
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
for(int i = 0; i < lenA; i++) ans[i] = a[i];
for(int i = 0; i < len; i++)
{
if(i < lenB)
tmp += (ans[i] - '0') + (b[i] - '0');
else
tmp += (ans[i] - '0');
ans[i] = tmp % 10 + '0';
tmp /= 10;
}
reverse(ans.begin(), ans.end());
return ans.substr(ans.find_first_not_of('0'));
}
string sub(string a, string b) // 保证 a 一定大于 b
{
a = a.substr(a.find_first_not_of('0'));
b = b.substr(b.find_first_not_of('0'));
long long lenA = a.length();
long long lenB = b.length();
long long len = max(lenA,lenB) + maxn;
int tmp = 0;
string ans(len, '0');
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
for(int i = 0; i < lenA; i++) ans[i] = a[i];
for(int i = 0; i < len; i++)
{
if(i < lenB)
tmp += (ans[i] - '0') - (b[i] - '0');
else
tmp += (ans[i] - '0');
if(tmp < 0)
{
ans[i] = (tmp + 10) % 10 + '0';
tmp = -1;
}
else
{
ans[i] = tmp % 10 + '0';
tmp = 0;
}
}
reverse(ans.begin(), ans.end());
return ans.substr(ans.find_first_not_of('0'));
}
int compare(string a, string b) // a > b return 1 a < b return -1
{
if(a.length() > b.length()) return 1;
else if(a.length() < b.length()) return -1;
else
{
if(a == b) return 0;
else if(a > b) return 1;
else return -1;
}
}
void divide(string a, string b, int num)
{
int tmp = 0;
while(compare(a, b) >= 1)
{
a = sub(a, b);
tmp++;
}
cout << tmp << ".";
while(num)
{
num--;
tmp = 0;
a.append("0");
while(compare(a, b) >= 1)
{
a = sub(a, b);
tmp++;
}
cout << tmp;
}
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
string son = "1", mother = "1";
for(int i = 1; i <= 505; i++)
{
string tmp = mother;
mother = add(son, mother);
son = tmp;
}
divide(son, mother, 101);
return 0;
}
前缀判断
C语言的基础题。
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn = 10;
char* prefix(char* haystack_start, char* needle_start)
{
char* haystack = haystack_start;
char* needle = needle_start;
while(*haystack && *needle){
if(*(haystack++) != *(needle++)) return NULL; //填空位置
}
if(*needle) return NULL;
return haystack_start;
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
char* haystack_start = "abc123";
char* needle_start = "abc";
cout << prefix(haystack_start, needle_start);
return 0;
}
三部排序
如果x[p] > 0, 那么直接交换到右边界,右边界确定了所以right- -,如果 x[p] < 0,就和左边界交换,左边界也确定了是负数所以left++,如果x[p] == 0,那么只用p继续向前移动即可,因为如果前面还有负数,还能将左边界的0交换到中间。
#include <iostream>
using namespace std;
const int maxn = 10;
void sort3p(int* x, int len)
{
int p = 0;
int left = 0;
int right = len-1;
while(p <= right)
{
if(x[p]<0)
{
int t = x[left];
x[left] = x[p];
x[p] = t;
left++;
p++;
}
else if(x[p]>0)
{
int t = x[right];
x[right] = x[p];
x[p] = t;
right--;
}
else
{
p++;
}
}
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
int x[14] = {25,18,-2,0,16,-5,33,21,0,19,-16,25,-3,0};
sort3p(x, 14);
for(int i = 0; i < 14; i++) cout << x[i] << " ";
return 0;
}
错误票据
简单题不多赘述。
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <sstream>
using namespace std;
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
int nn;
cin>>nn;
getchar();
int cnt=0;
int x[10005];
for(int i=1;i<=nn;i++)
{
string s;
getline(cin,s);
//cout<<s<<endl;
stringstream ss;
ss<<s;
while(ss>>x[cnt++]) ;
}
int n,m;
sort(x,x+cnt);
for(int i=1;i<cnt;i++)
{
//cout<<x[i]<<endl;
if(x[i]==x[i-1]) m=x[i];
else
{
if(x[i]-x[i-1]!=1) n=x[i-1]+1;
}
}
cout<<n<<" "<<m<<endl;
return 0;
}
翻硬币
这道题用bfs做会超时,所以通过找到一个规律来求解。
例如,有两个串,***** ***** 和 o**** o****,第一个硬币不同的位置为1,第二个硬币不同的位置为6,最小翻动次数即为6 - 1 = 5.如果有多段不同的序列,则将不同序列的和相加。
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
string s1, s2;
int start = -1;
int ans = 0;
cin >> s1 >> s2;
for(int i = 0; i < s1.length(); i++)
{
if(s1[i] != s2[i])
{
if(start == -1)
{
start = i;
}
else
{
ans += (i - start);
start = -1;
}
}
}
cout << ans << endl;
return 0;
}
带分数
直接dfs求出整个解空间,然后找出符合条件的解。
dfs的参数解释:
vis[]数组用来记录当前访问过的数字,确保1-9只使用一次。
divide1,divide2分别是被除数和除数。
dividenum1用来控制被除数位数,假如 整数部分是1,那么分数部分就有8位。dividenum1 = 2,表示得到一个2位的被除数后,开始枚举一个6位的除数。
nowDivideNum1初始化为0,表示当前被除数已经有多少位。当nowDivideNum1 == dividenum1 - 1时, 开始枚举除数。
intger表示带分数的整数部分
nownum表示现在还剩多少位数字没有使用,如果等于0,则说明得到了一个带分数。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
const int maxn = 11;
int n;
int ans;
void dfs(bool vis[], int divide1, int divide2, int divideNum1, int nowDivideNum1, int intger, int nownum)
{
if(nownum == 0 && intger + divide1/divide2 == n && (divide1/divide2) * divide2 == divide1)
{
//cout << intger << " + " << divide1 << "/" << divide2 << endl;
ans++;
return ;
}
else
{
if(nowDivideNum1 < divideNum1)
{
for(int i = 1; i <= 9; i++)
{
if(!vis[i])
{
int tmp = divide1 * 10 + i;
vis[i] = true;
dfs(vis, tmp, divide2, divideNum1, nowDivideNum1+1, intger, nownum-1);
vis[i] = false;
}
}
}
else
{
for(int i = 1; i <= 9; i++)
{
if(!vis[i])
{
int tmp = divide2 * 10 + i;
vis[i] = true;
dfs(vis, divide1, tmp, divideNum1, nowDivideNum1, intger, nownum-1);
vis[i] = false;
}
}
}
}
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
bool vis[maxn];
ans = 0;
cin >> n;
for(int i = 1; i <= n; i++)
{
memset(vis, false, sizeof(vis));
int num = i;
int flag = 1;
int Count = 0;
while(num) //判断num是否可以作为带分数的整数部分并且记录整数部分的位数
{
if(num%10 == 0 || vis[num%10] == true)
{
flag = 0;
break;
}
else
vis[num%10] = true;
num /= 10;
Count++;
}
if(flag)
{
//cout << "i = " << i << endl;
for(int j = 1; j <= 8-Count; j++)
{
dfs(vis, 0, 0, j, 0, i, 9-Count);
}
}
}
cout << ans << endl;
return 0;
}
连号区间数
这道题的数据很水,所以O(n²)的写法也能过, 没有太大意义。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
const int maxn = 50005;
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
int a[maxn];
int n;
int ans = 0;
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++)
{
int Min = a[i];
int Max = a[i];
for(int j = i; j <= n; j++)
{
if(i == j)
{
ans++;
}
else
{
Min = min(Min, a[j]);
Max = max(Max, a[j]);
if(Max - Min == j - i) ans++;
}
}
}
cout << ans << endl;
return 0;
}
2014年省赛真题
啤酒与饮料
三个小数都扩大十倍变成整数运算。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
const int maxn = 50005;
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
int beer = 0;
int drink = 1;
for(;; drink++)
for(beer = 0; beer < drink; beer++)
{
if(drink*19 + beer*23 == 823)
{
cout << "beer = " << beer << " drink = " << drink << endl;
return 0;
}
}
//return 0;
}
切苗条
找规律,切n次有 pow(2, n) + 1段。
#include <iostream>
#include <math.h>
using namespace std;
const int maxn = 50005;
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
cout << (int)pow(2,10) + 1 << endl;
return 0;
}
李白打酒
dfs所有结果就行。
#include <iostream>
#include <math.h>
using namespace std;
int ans = 0;
void dfs(int beer, int shop, int flower)
{
if(beer <= 0) return ;
if(!shop && flower == 1)
{
if(beer - 1 == 0)
ans++;
return;
}
if(shop > 0) dfs(beer*2, shop-1, flower);
if(flower > 0) dfs(beer-1, shop, flower-1);
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
dfs(2, 5, 10);
cout << ans << endl;
return 0;
}
奇怪的分式
不要用分数的除法来比较,会丢失精确度。先算出两个分数的最简式,然后分子分母都相同则分数相同。
#include <iostream>
#include <math.h>
using namespace std;
int gcd(int a, int b)
{
return (!b)? a:gcd(b, a%b);
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
int ans = 0;
for(int son1 = 1; son1 <= 9; son1++)
for(int mother1 = 1; mother1 <= 9; mother1++)
{
if(son1 == mother1) continue;
for(int son2 = 1; son2 <= 9; son2++)
for(int mother2 = 1; mother2 <= 9; mother2++)
{
if(son2 == mother2) continue;
int son3 = son1*10 + son2;
int mother3 = mother1*10 + mother2;
int son4 = son1 * son2;
int mother4 = mother1 * mother2;
int gcd3 = gcd(son3, mother3);
int gcd4 = gcd(son4, mother4);
son3 /= gcd3;
mother3 /= gcd3;
son4 /= gcd4;
mother4 /= gcd4;
if(son3 == son4 && mother3 == mother4) ans++;
}
}
cout << ans << endl;
return 0;
}
打印图形
考虑rank = 6 的情况,先把打印出的图片看作三个大的三角形,上面一个,下面两个。然后给出的递归式打印的是下面两个,因此只要处理上面的一个大三角形。每次输入的都是大三角形的左上顶点。
#include <iostream>
#include <math.h>
#include <stdio.h>
using namespace std;
#define N 70
void f(char a[][N], int Rank, int row, int col)
{
//cout << "Rank = " << Rank << " row = " << row << " col = " << col << endl;
if(Rank == 1)
{
//cout << " row = " << row << " col = " << col << endl;
a[row][col] = '*';
return;
}
int w = 1;
int i;
for(i = 0; i< Rank-1; i++) w *= 2;
f(a, Rank-1, row, col+w/2); //填空
f(a, Rank-1, row+w/2, col); // a, 5, 16, 0
f(a, Rank-1, row+w/2, col+w); // a, 5, 16, 32
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
char a[N][N];
int i, j;
for(i = 0;i < N;i++)
for(j = 0;j < N;j++) a[i][j] = ' ';
f(a, 6, 0, 0);
for(i = 0; i < N; i++)
{
for(j = 0; j < N; j++) printf("%c", a[i][j]);
printf("\n");
}
return 0;
}
地宫取宝
这题用普通的dfs会超时,需要记忆化搜索。
用一个四维数组cache[x][y][Maxci][choicenum]表示一个点在取MaxCi值时的choicenum种数,从而避免了一些情况的重复搜索。
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
const int maxn = 55;
const long long mod = 1000000007;
int n, m, k;
int Map[maxn][maxn];
int cache[maxn][maxn][14][13];
int dx[2] = {0, 1};
int dy[2] = {1, 0};
long long dfs(int x, int y, int MaxCi, int choicenum)
{
long long ans = 0;
//cout << "x = " << x << " y = " << y << " choicenum = " << choicenum << endl;
if(cache[x][y][MaxCi+1][choicenum] != -1) return cache[x][y][MaxCi+1][choicenum];
if(x == n && y == m)
{
if(choicenum == k || (choicenum == k-1 && Map[n][m] > MaxCi))
{
ans = (ans+1) % mod;
return ans;
}
return 0;
}
for(int i = 0; i < 2; i++)
{
int xx = x + dx[i];
int yy = y + dy[i];
if(xx <= n && yy <= m)
{
if(Map[x][y] > MaxCi)
ans += dfs(xx, yy, Map[x][y], choicenum+1);
ans += dfs(xx, yy, MaxCi, choicenum);
}
}
cache[x][y][MaxCi+1][choicenum] = ans % mod;
return ans % mod;
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
cin >> n >> m >> k;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) cin >> Map[i][j];
memset(cache, -1, sizeof(cache));
cout << dfs(1, 1, -1, 0) << endl;
return 0;
}
六角填数
按下图方式处理图形:图中黑子数字编号代表搜索顺序,蓝色数字代表六条直线的编号,用来计算直线之和,然后dfs即可。
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
int line[6];
int ans;
bool vis[13];
int Map[9][6] = {
{1, 0, 0, 1, 0, 0},
{1, 0, 0, 0, 1, 0},
{0, 1, 0, 0, 1, 0},
{0, 1, 0, 0, 0, 1},
{0, 0, 1, 0, 0, 1},
{0, 0, 1, 1, 0 ,0},
{1, 1, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 1},
{0, 0, 0, 1, 0, 1}
};
void dfs(int num)
{
if(num == 0)
{
int flag = 1;
for(int i = 0; i < 5; i++)
{
if(line[i] != line[i+1])
{
flag = 0;
break;
}
}
if(flag)
cout << ans << endl;
return ;
}
for(int i = 1; i <= 12; i++)
{
if(!vis[i])
{
if(num == 4) ans = i;
vis[i] = true;
for(int j = 0; j < 6; j++)
{
if(Map[9-num][j])
line[j] += i;
}
dfs(num-1);
vis[i] = false;
for(int j = 0; j < 6; j++)
{
if(Map[9-num][j])
line[j] -= i;
}
}
}
}
void init()
{
memset(vis, false, sizeof(vis));
memset(line, 0, sizeof(line));
vis[1] = vis[3] = vis[8] = true;
line[0] = 8;
line[1] = 3;
line[2] = 11;
line[3] = line[4] = 1;
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
init();
dfs(9);
return 0;
}
史丰收运算
根据填空部分while循环的代码来看,r变量等于0时继续循环,r变量小于0时满足大于某一位的条件,所以填空部分处理的应该是r变量小于0 的时候的返回值。例如取900000和840000两个值时,i都是从5开始循环递减。900000> 857142,满足大于的条件返回i+1即6,而840000< 857142因此返回 i 就行。
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
//计算个位
int ge_wei(int a)
{
if(a % 2 == 0)
return (a * 2) % 10;
else
return (a * 2 + 5) % 10;
}
//计算进位
int jin_wei(char* p)
{
char* level[] = {
"142857",
"285714",
"428571",
"571428",
"714285",
"857142"
};
char buf[7];
buf[6] = '\0';
strncpy(buf,p,6);
int i;
for(i = 5; i >= 0; i--)
{
int r = strcmp(level[i], buf);
if(r < 0) return i+1;
while(r == 0)
{
p += 6;
strncpy(buf,p,6);
r = strcmp(level[i], buf);
if(r < 0) return i+1;
if(r > 0) return i;//填空
}
}
return 0;
}
//多位数乘以7
void f(char* s)
{
int head = jin_wei(s);
if(head > 0) printf("%d", head);
char* p = s;
while(*p)
{
int a = (*p-'0');
int x = (ge_wei(a) + jin_wei(p+1)) % 10;
printf("%d",x);
p++;
}
printf("\n");
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
f("428571428571");
f("34553834937543");
return 0;
}
蚂蚁感冒
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
struct point
{
int x;
bool a;
int t;
}p[maxn];
bool cmp(point p1,point p2)
{
return p1.x<p2.x;
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
int n;
cin >> n;
point s;
cin >> p[1].x;
if(p[1].x < 0)
{
p[1].x = -p[1].x;
p[1].a = true;
p[1].t = -1;
}
else
{
p[1].a = true;
p[1].t =1;
}
for(int i = 2;i <= n;i++)
{
cin >> p[i].x;
if(p[i].x < 0)
{
p[i].x = -p[i].x;
p[i].a = false;
p[i].t = -1;
}
else
{
p[i].a = false;
p[i].t = 1;
}
}
sort(p+1, p+1+n, cmp);
while(1)
{
int Min = 999;
int start;
for(int i = 2;i <= n;i++)
{
if(p[i-1].t == 1 && p[i].t == -1 && (p[i].x-p[i-1].x) < Min)
{
Min = p[i].x-p[i-1].x;
start = i-1;
}
}
if(Min == 999) break;
else
{
for(int i = 2;i <= n;i++)
{
if(p[i-1].t == 1 && p[i].t == -1 && (p[i].x-p[i-1].x) == Min)
{
if(p[i-1].a == true || p[i].a == true)
p[i-1].a = p[i].a = true;
p[i-1].t = -p[i-1].t;
p[i].t = -p[i].t;
}
else if(i != start && i != start + 1)
{
p[i].x += p[i].t * Min;
}
}
if(start != 1) p[1].x += p[1].t * Min;
}
}
int ans = 0;
for(int i = 1;i <= n;i++)
if(p[i].a == true) ans++;
cout << ans << endl;
return 0;
}
小朋友排队
这道题有冒泡排序的的味道,但用冒泡排序会超时。看大部分博客都是用树状数组做,本人只学过线段树,这里就用线段树做。
首先要明白这道题就是找逆序对的个数,对于用线段树的做法,那么就是遍历数组中的每一个数,从这个数往后找所有比它小的数(这样就一定要交换一次),从这个数往前找所有比它大的数(同样是逆序对)。所以先建一次树,然后再用一个数组记录第一种情况,对于每个数Hi, 就用线段树统计【1, Hi-1】区间内的个数,然后再建一次树处理第二种情况。
注意用线段树做本题的坑点:
(1) 用于记录逆序对的数组必须是long long型,不然计算不高兴值时会超出范围。
(2) 身高值可以为0,因此每次数组的身高都加1,便于建立线段树。
(3) 遇到身高为1(身高为0的小朋友加了1后)的数据,需要跳过,不然线段树query函数会出错。而且continue不能放在for循环开始,应该等到update后再continue。
下面给出一个样例。
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
const int maxn = 100000;
struct node
{
int l,r;
int sum;
}T[maxn*10*4+5];
int Hi[maxn*10+5];
int Hx[maxn+5];
long long cnt[maxn+5]; // 1
long long ans;
void build(int left, int right, int num)
{
T[num].l = left;
T[num].r = right;
if(left == right)
{
//cout << "left = " << left << " Hi[left] = " << Hi[left] << endl;
T[num].sum = Hi[left];
return;
}
int mid = (left + right) / 2;
build(left, mid, 2*num);
build(mid+1, right, 2*num+1);
T[num].sum = T[2*num].sum + T[2*num+1].sum;
}
void update(int x, int num)
{
//cout << "T[num].l = " << T[num].l << " T[num].r = " << T[num].r << " num = " << num << endl;
T[num].sum--;
if(T[num].l == T[num].r) return ;
int mid = (T[num].l + T[num].r) / 2;
if(x <= mid)
update(x, 2*num);
else
update(x, 2*num+1);
}
void query(int x, int y, int num)
{
if(x == T[num].l && y == T[num].r)
{
ans += T[num].sum;
return ;
}
int mid = (T[num].l + T[num].r) / 2;
if(y <= mid)
query(x, y, 2*num);
else if(x > mid)
query(x, y, 2*num+1);
else
{
query(x, mid, 2*num);
query(mid+1, y, 2*num+1);
}
}
void Search(int num)
{
cout << "left = " << T[num].l << " right = " << T[num].r << " sum = " << T[num].sum << " num = " << num << endl;
if(T[num].l == T[num].r) return;
Search(2*num);
Search(2*num+1);
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
int n;
memset(Hi, 0, sizeof(Hi));
memset(cnt, 0, sizeof(cnt));
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> Hx[i];
Hx[i] += 1;//2
Hi[Hx[i]]++;
}
build(1, maxn*10+5, 1);
//Search(1);
for(int i = 1; i <= n; i++)
{
ans = 0;
update(Hx[i], 1);
if(Hx[i] == 1) continue; // 3
//if(i == 7) Search(1);
query(1, Hx[i]-1, 1);
cnt[i] += ans;
//cout << "cnt[i] = " << cnt[i] << " Hx[i] = " << Hx[i] << endl;
}
build(1, maxn*10+5, 1);
for(int i = n; i >= 1; i--)
{
ans = 0;
update(Hx[i], 1);
query(Hx[i]+1, maxn*10+5, 1);
cnt[i] += ans;
//cout << "cnt[i] = " << cnt[i] << endl;
}
ans = 0;
for(int i = 1; i <= n; i++)
ans += (1 + cnt[i])*cnt[i] / 2;
cout << ans << endl;
return 0;
}
2015年省赛真题
奖券数目
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
const int maxn = 100000;
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
int sum = 0;
for(int i = 10000; i <= 99999; i++)
{
if(i >= 40000 && i <= 49999) continue;
int tmp = i;
int flag = 1;
while(tmp)
{
if(tmp % 10 == 4)
{
flag = 0;
break;
}
tmp /= 10;
}
if(flag) sum++;
}
cout << sum << endl;
return 0;
}
星系炸弹
2017-08-05.
三羊献瑞
枚举。
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
for(int A = 1; A <= 9; A++)
{
for(int B = 0; B <= 9; B++)
{
if(A == B) continue;
for(int C = 0; C <= 9; C++)
{
if(A == C || B == C) continue;
for(int D = 0; D <= 9; D++)
{
if(A == D || B == D || C == D) continue;
for(int E = 1; E <= 9; E++)
{
if(A == E || B == E || C == E || D == E) continue;
for(int F = 0; F <= 9; F++)
{
if(A == F || B == F || C == F || D == F || E == F) continue;
for(int G = 0; G <= 9; G++)
{
if(A == G || B == G || C == G || D == G || E == G || F == G) continue;
for(int H = 0; H <= 9; H++)
{
if(A == H || B == H || C == H || D == H || E == H || F == H || G == H) continue;
int add1 = A*1000 + B*100 + C*10 + D;
int add2 = E*1000 + F*100 + G*10 + B;
int add3 = E*10000 + F*1000 + C*100 + B*10 + H;
//cout << "add1 = " << add1 << " add2 = " << add2 << " add3 = " << add3 << endl;
if(add1 + add2 == add3)
{
cout << " " << A << " " << B << " " << C << " " << D << endl;
cout << "+ " << E << " " << F << " " << G << " " << B << endl;
cout << E << " " << F << " " << C << " " << B << " " << H << endl;
return 0;
}
}
}
}
}
}
}
}
}
return 0;
}
格子里输出
%*s表示输出字符串至少有X个长度,不足的长度补空格。
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
void StringInGrid(int width, int height, const char* s) // 20 6 abcd1234 //8
{
int i,k;
char buf[1000];
strcpy(buf, s);
if(strlen(s) > width - 2) buf[width-2] = 0;
printf("+");
for(i = 0; i < width - 2; i++) printf("-");
printf("+\n");
for(k=1; k < (height - 1) / 2; k++)
{
printf("|");
for(i = 0; i < width - 2; i++) printf(" ");
printf("|\n");
}
printf("|");
printf("%*s%s%*s",(width - 2 - strlen(buf)) / 2, "",buf, (width - 1 - strlen(buf)) / 2, ""); //填空
printf("|\n");
for(k = (height - 1) / 2 + 1; k < height - 1; k++)
{
printf("|");
for(i = 0; i < width - 2; i++) printf(" ");
printf("|\n");
}
printf("+");
for(i = 0; i < width-2; i++) printf("-");
printf("+\n");
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
StringInGrid(20, 6, "abcd1234");
return 0;
}
九数组分数
典型回溯问题,递归前改变值,递归后将值变回去。
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
void test(int x[])
{
int a = x[0]*1000 + x[1]*100 + x[2]*10 +x[3];
int b = x[4]*10000 + x[5]*1000 + x[6]*100 +x[7]*10 + x[8];
if(a * 3 == b)
printf("%d / %d\n", a,b);
}
void f(int x[],int k)
{
int i,t;
if(k >= 9)
{
test(x);
return;
}
for(i = k; i < 9; i++)
{
{t = x[k];x[k] = x[i];x[i] = t;}
f(x,k+1);
{t = x[k];x[k] = x[i];x[i] = t;}// 填空处
}
}
/*
1,2,3…9 这九个数字组成一个分数,其值恰好为1/3,如何组法?
下面的程序实现了该功能,请填写划线部分缺失的代码。
*/
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout);
int x[] = {1,2,3,4,5,6,7,8,9};
f(x,0);
return 0;
}
加法变乘法
枚举。
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
const int maxn = 50;
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout)
for(int i = 1; i < maxn-1; i++)
{
for(int j = i+2; j < maxn-1; j++)
{
int sum = 1225 + j*(j+1) - j - (j+1) + i*(i+1) - i - (i+1);
if(sum == 2015)
cout << i << endl;
}
}
return 0;
}
移动距离
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
using namespace std;
int abs(int a, int b)
{
if(a > b) return a-b;
else return b-a;
}
int main()
{
//freopen("d:in.txt","r",stdin);
//freopen("d:testout.txt","w",stdout)
int w, m, n;
while(~scanf("%d%d%d", &w, &m, &n))
{
int row1, col1;
int row2, col2;
if(m/w*w != m) row1 = m/w + 1;
else row1 = m/w;
if(n/w*w != n) row2 = n/w + 1;
else row2 = n/w;
if(row1%2 == 0)
col1 = w - (m-1)%w;
else
col1 = (m%w == 0? w:m%w);
if(row2%2 == 0)
col2 = w - (n-1)%w;
else
col2 = (n%w == 0? w:n%w);
//cout << "row1 = " << row1 << " row2 = " << row2 << " col1 = " << col1 << " col2 = " << col2 << endl;
cout << abs(row1, row2) + abs(col1, col2) << endl;
}
return 0;
}