文章目录
2015
隔行变色 (小学题)
奇数行为蓝色
想知道21到50行有多少蓝色的行
#include <iostream>
using namespace std;
int test_01() {
int ans = 0;
for(int i = 21; i <= 50; i++) {
if(i % 2) {
ans++;
}
}
printf("%d\n",ans);
return 0;
}
立方尾不变 (时间复杂度!!)
有些数字的末尾正好是该数字本身
如:1,4,5,6,9,24,25,…
请计算10000以内的符合这个特征的正整数[1,9999]有多少
立方数值 long long !!!
#include<sstream> //字符串输入输出流
#include<cstring>
/*
将int类型的值放入输入流中
sstream << nValue;
从sstream中抽取前面插入的int类型的值,赋给string类型
sstream >> strResult;
*/
/*bug............
int ans = 0;
void check(ll ii,ll i){
string iistr,istr;
stringstream ss1;
ss1 << ii; //
ss1 >> iistr; // (转字符串)
stringstream ss2;
ss2 << i;
ss2 >> istr;
if(iistr.substr(iistr.length()-istr.length(),iistr.length()) == istr){ //最后几位等于本身
ans++;
}
}
int test_02(){
for(int i = 0;i < 10000;i++){
check(i*i*i,i);
}
cout << ans << endl;
return 0;
}
*/
typedef long long ll;
//取最后n位三次方后的末尾 == 本身 即可 (n为这个数的位数 取数用 % 10^n^)
int test_02_2() {
int ans = 0;
for(ll i = 1; i < 10000; i++) { //正整数
if(i < 10) {
if(i*i*i%10 == i )ans++; //这里提前会运算错误!!! ....... 没有达到64位没必要提前
} else if(i < 100) {
if(i*i*i%100 == i )ans++;
} else if(i < 1000) {
if(i*i*i%1000 == i )ans++;
} else if(i < 10000) {
if(i*i*i%10000 == i )ans++;
}
}
cout << ans << endl;
return 0;
}
三羊献瑞
观察下面的加法算式:
祥 瑞 生 辉
+ 三 羊 献 瑞
------------------
三 羊 生 瑞 气
其中,相同的汉字代表相同的数字,不同的汉字代表不同的数字。(注意都不同!!)
请你填写“三羊献瑞”所代表的4位数字(答案唯一),不要填写任何多余内容。
i d x j + a b c d ———————————— a b x d k
#先观察!! 最高位和得出 ‘三’= 1,而 ‘祥’ + ‘三’ 要进位则必须 ‘祥’ = 9 – > ‘羊’ = 0 ,‘生’ = ‘瑞’ + 1 , ‘生’ + ‘献’ > 10
----------------------------------------------------------------------------my思路
?d生? + abcd = ab生d? //i j k对应依次对应3个问号位置
l1 + l2 = r
*/
//int l1[105],l2[105],r[105];
bool check_03(int a,int b,int c,int d) {
int l1,l2,r;
int x; //生
for(int i = 1; i < 10; i++) {//最高位 != 0 (重要)
for(int j = 0; j < 10; j++) {
if(i != j && i != a && i != b && i != c &&i != d ) {
for(int x = 0; x < 10; x++) {
if(x != a &&x != b &&x != c &&x != d &&x != i &&x != j ) {
l1 = i * 1000 + d * 100 + x * 10 + j;
l2 = a * 1000 + b * 100 + c * 10 + d;
for(int k = 0; k < 10; k++) {
if(k != a && k != b && k != c && k != d && k != i && k != j && k != x ) {
r = a * 10000 + b * 1000 + x * 100 + d * 10 + k;
if(l1 + l2 == r) {
return true;
}
}
}
}
}
}
}
}
return false;
}
int test_03() { //想法: 全排列 1234 ,2345,3456 ,4567,5678,6789 每次用next_permutation取试试就可以了,填空题!
int a,b,c,d; //a != 0 1234 - 6789
int temp;
for(int a = 1; a < 10; a++) {
for(int b = 0; b < 10 ; b++) {
if(a != b) {
for(int c = 0; c < 10; c++) {
if(c != a && c!= b) {
for(int d = 0; d < 10; d++) {
if(d != a && d!= b && d!= c) {
//可以再加个条件:每位上的两数和>10 剪枝 && >10
if(check_03(a,b,c,d)) {
temp = a * 1000 + b * 100 + c * 10 + d;
break;
}
}
}
}
}
}
}
}
cout << temp << endl;
//cout << a << b <<c << d <<endl; 有bug... // 或可以printf("%d%d%d%d",a,b,c,d);
return 0;
}
格子中输出 (代码补全 - 跑代码验证)
偏题 %*s *号指代宽度 (多一个参数) printf(%*s ,5,string) 等效 printf(“%5s”,string);
StringInGrid函数会在一个指定大小的格子中打印指定的字符串。
要求字符串在水平、垂直两个方向上都居中。
如果字符串太长,就截断。
如果不能恰好居中,可以稍稍偏左或者偏上一点。
下面的程序实现这个逻辑,请填写划线部分缺少的代码。
#include <stdio.h>
#include <string.h>
void StringInGrid(int width, int height, const char* s)
{
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",_____________________________________________); //填空
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()
{
StringInGrid(20,6,"abcd1234");
return 0;
}
/*
答案:printf("%*s%s%*s",(width - strlen(buf)-2)/2 ,"",buf,(width - strlen(buf)-2)/2,"");
*/
串逐位和 (代码补全)
给定一个由数字组成的字符串,我们希望得到它的各个数位的和。
比如:“368” 的诸位和是:17
这本来很容易,但为了充分发挥计算机多核的优势,小明设计了如下的方案:
int f(char s[], int begin, int end)
{
int mid;
if(end-begin==1) return s[begin] - '0';
mid = (end+begin) / 2;
return ____________________________________; //填空
}
int main()
{
char s[] = "4725873285783245723";
printf("%d\n",f(s,0,strlen(s)));
return 0;
}
/*
你能读懂他的思路吗? 请填写划线部分缺失的代码。
注意:只填写缺少的部分,不要填写已有代码或任何多余内容。
答案:f(s,begin,mid)+f(s,mid,end) 跑出来+计算器验证,或者敲代码验证 (二分查找-递归):分解成子问题 递归求解
*/
奇妙的数字
小明发现了一个奇妙的数字。它的平方和立方正好把0~9的10个数字每个用且只用了一次。
你能猜出这个数字是多少吗?
请填写该数字,不要填写任何多余的内容。
先分析:推测平方4位,立方6位数 ,用计算器缩小遍历范围 47 - 100,数据小可以把数字打出来肉眼观察找到
int test_06() { //暴力枚举10个位不相等 -- 这里只是排除了一些(很长没打全,共10位),但已经缩短很少了 ,特别是刚开始定i的范围 !!!
for(int i = 47; i < 100; i++) {
if(i*i%10 != i*i /10 % 10 &&i*i % 10 != i*i / 1000 &&i*i % 10 != i*i / 100 % 10 && i*i /10 % 10 != i*i / 1000 )
if(i*i*i % 10 != i*i*i /10%10 &&i*i*i % 10 != i*i*i /100 % 10 && i*i*i % 10 != i*i*i /1000 )
cout << i*i << " "<<i*i*i << endl; //4761 328509 -- > 69 * 69
}
//cout << 69 << endl;
return 0;
}
/*法二:整型转字符串输入输出流 + set去重特性
ss.size() == 10 用来check
*/
#include<sstream>
#include<set>
void i2s(int num,string &str) { //int --> string
stringstream ss;
ss << num;
ss >> str;
}
bool check_2(string s) { // 利用set去重 如果s.size() == 10 成功
set<char> ss;//变量名不重复
for(int i = 0; i < s.length(); i++) {
ss.insert(s[i]); //set是insert !!!
}
if(ss.size()==10) {
return true;
}
return false;
}
int test_06_2() {
for(int i = 1; i < 100; i++) {
string s1,s2;
i2s(i*i,s1);
i2s(i*i*i,s2);
if(check_2(s1+s2)) { // 字符串拼接 ' + '
cout << i << endl;
break;
}
}
return 0;
}
加法变乘法
我们都知道:1+2+3+ … + 49 = 1225
现在要求你把其中两个不相邻的加号变成乘号,使得结果为2015
比如:
1+2+3+…+1011+12+…+2728+29+…+49 = 2015
就是符合要求的答案。
请你寻找另外一个可能的答案,并把位置靠前的那个乘号左边的数字提交(对于示例,就是提交10)。
注意:需要你提交的是一个整数,不要填写任何多余的内容。
ans = 16 (其他不变 + 16*17 + 24 * 25 = 2015)
int test_07() {
int a[55];
for(int i = 0; i < 49; i++) {
a[i] = i + 1;
}
int sum = 0;
for(int i = 0; i < 49; i++) {//已经给了1225,可以不用
sum += a[i];
}
int temp; // 其他方法:给第二个乘号留位置乘号,最多到46!!!
for(int i = 0; i < 47; i++) { //每次减去两边的乘数 + 乘积 my_边界判断 最多 46 * 47 + 48 * 49
for(int j = 0; j < 49; j++) {
if(j != i && j != i+1) { // 1与3、4、5、6...50项 , 2与 4,5,6...50项 //也可以 j >= i + 2
temp = sum - a[i] - a[i+1] - a[j] - a[j+1] + a[i] * a[i+1] + a[j] * a[j+1];
if(temp == 2015) {
cout << a[i] << " "<< a[i+1] << endl; //输出输入流的 正确多输出法!!!
break;
}
temp = sum; //变回去重新判断!!!
}
}
}
return 0;
}
/*最简洁写法*/
for(int i = 1;i <= 46;i++){
for(int j = i+2;j <= 48;j++){
if(i*(i+1) + j*(j+1) -(i+i+1)-(j+j+1) == 2015-1225 ){
cout << a[i] << " "<< a[j] << endl;
}
}
}
饮料换购
乐羊羊饮料厂正在举办一次促销优惠活动。乐羊羊C型饮料,凭3个瓶盖可以再换一瓶C型饮料,并且可以一直循环下去,但不允许赊账。
请你计算一下,如果小明不浪费瓶盖,尽量地参加活动,那么,对于他初始买入的n瓶饮料,最后他一共能得到多少瓶饮料。
输入:一个整数n,表示开始购买的饮料数量(0<n<10000)
输出:一个整数,表示实际得到的饮料数
例如:
用户输入:
100
程序应该输出:
149
用户输入:
101
程序应该输出:
151
先思考:
100 + (((100/3 + 100%3) /3 + 34 % 3) / 3 + 12 % 3) / 3 + 4 % 3 此时只剩2瓶结束
思路2:每喝掉3瓶 n-2 ,ans += 3 ,最后n < 3 时 ,还能再喝n ,ans += n
/*my_thinking...........................bug
void dfs(int n,int ans) { // n >= 3时
if(n < 3) {
cout << ans << endl;
return;
}
dfs(n / 3,ans + n / 3 + n % 3);
}
*/
/*
if(n == 1 || n == 2){
cout << n << endl;
return 0;
}
ans = n;
int temp;
while(n>=3){ //while在仅想条件的时候用
temp = n / 3 + n % 3;
if(temp>=3){
ans += temp;
}
n = n/3 + n % 3;
}
if(n == 1 || n == 2){
cout << ans << endl;
return 0;
}
*/
/*
if(n == 1 || n == 2) {
cout << n << endl;
return 0;
} else {
dfs(n,n);
}
*/
int test_08() {
int n,ans = 0;
scanf("%d",&n);
while(n >= 3) {
n -= 2;
ans += 3;
}
ans += n;
cout << ans << endl;
return 0;
}
打印大X (打印图形 - 规律模拟)
小明希望用星号拼凑,打印出一个大X,他要求能够控制笔画的宽度和整个字的高度。
为了便于比对空格,所有的空白位置都以句点符来代替。
要求输入两个整数m n,表示笔的宽度,X的高度。用空格分开(0<m<n, 3<n<1000, 保证n是奇数)
要求输出一个大X
发现规律:列数n+m-1 对应下标(0 – n+m-2)
[0,m-1+t +i ] ,[n+m-2 -t -i , n+m-2] 填 ‘*’
/*
例如,用户输入:
3 9
程序应该输出:
***.....***
.***...***.
..***.***..
...*****...
....***....
...*****...
..***.***..
.***...***.
***.....***
再例如,用户输入:
4 21
程序应该输出
****................****
.****..............****.
..****............****..
...****..........****...
....****........****....
.....****......****.....
......****....****......
.......****..****.......
........********........
.........******.........
..........****..........
.........******.........
........********........
.......****..****.......
......****....****......
.....****......****.....
....****........****....
...****..........****...
..****............****..
.****..............****.
****................****
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。
规律:列数n+m-1 对应下标(0 -- n+m-2) [0,m-1+t +i ] ,[n+m-2 -t -i , n+m-2] 填 '*'
列数(宽度) w =m + (n/2)*2 int除二省略小数 == m + n - 1
*/
char a[500][1000];
int test_09() {
int m,n;
scanf("%d%d",&m,&n);
for(int i = 0; i < n; i++) {
for(int j = 0; j < n + m - 1; j++) { //相对位移n行 -- 发现图形 n*(n+m-1)
a[i][j] = '.';
}
}
//int k = 0; //相对位移增量 用i代替就好了
for(int i = 0; i < n; i++) {
for(int t = 0; t < m; t++) {
a[i][i+t] = '*';
a[i][(n+m-2)- t - i] = '*';
}
}
for(int i = 0; i < n; i++) {
for(int j = 0; j < n + m - 1; j++) {
cout<< a[i][j] ;
}
cout << endl;
}
return 0;
}
int main() {
test_09();
return 0;
}
垒骰子 【压轴】(模拟 | dp | 矩阵乘法(快速幂) )
赌圣atm晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体。
经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥!
我们先来规范一下骰子:1 的对面是 4,2 的对面是 5,3 的对面是 6。
假设有 m 组互斥现象,每组中的那两个数字的面紧贴在一起,骰子就不能稳定的垒起来。 atm想计算一下有多少种不同的可能的垒骰子方式。
两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都相同。
由于方案数可能过多,请输出模 1e9 + 7 的结果。
不要小看了 atm 的骰子数量哦~
「输入格式」
第一行两个整数 n m
n表示骰子数目
接下来 m 行,每行两个整数 a b ,表示 a 和 b 不能紧贴在一起。
「输出格式」
一行一个数,表示答案模 109 + 7 的结果。
「样例输入」
2 1
1 2
「样例输出」
544
对于 30% 的数据:n <= 5
对于 60% 的数据:n <= 100
对于 100% 的数据:0 < n <= 109, m <= 36
分析数据: 544 = (36 - 2)44 (减2是因为冲突2种) (两个骰子,上面的转下面不动,或者下面转上面不动 每确定一个可以旋转44种方向)
猜测每一种转4个方向,且排去下面和上面的冲突,4终可选 44 , 必是4的倍数
分支递归下一次放不能冲突 conflict 取对应的冲突值二维数组标记!!
法一: 模拟递归
f函数:上一层定好了朝上的数字为up的情况下,垒好cnt个骰子的方案数 (下层不断确定 ,不断选择上层 ,最终返回到底层,累加所有ans)
#include<cstdio>
const int mod = 1e9 + 7;
int op_1[7] = {0,4,5,6,1,2,3}; //规范骰子 用来获取骰子的对面 op_1[1] = 4 ,op_1[4] = 1 ...
bool conflict[7][7];
long long f(int up,int cnt){ //选择某一面时,统计某一面的ans
if(cnt == 0){//每个面可以旋转4次最后 4 * f () //或者到1 ,16 * f 上下各个翻转4*4
return 4;
}
long long ans = 0;
for(int upp = 1;upp <= 6;upp++){ //注意骰子是对面不冲突!选择up作为底面后,op_1转成对面判断是否冲突
if(conflict[ op_1[up] ][upp])continue; //check是否为冲突数 ,是就跳过
ans += f(upp,cnt - 1);
}
return ans;
}
int test_10(){
int n,m;
scanf("%d%d",&n,&m);
for(int i = 0;i < m;i++){
int x,y;
scanf("%d%d",&x,&y);
conflict[x][y] = true;//冲突数标记 !!!
conflict[y][x] = true;
}
long long ans = 0;
for(int up = 1;up <= 6;up++){ //特殊,递归刚开始就有6种情况
ans = ( ans + 4 * f(up,n-1) ) % mod; //题目要求 !!
}
printf("%lli",ans); //%lli
return 0;
}
法二:dp – 逆向递归
>dp[i][j] 表示在第i层j朝上的方案数
x[1,6] && x与op[j]不冲突 的前提 ∑dp[i - 1][x]
*/
long long dp[2][7];
#include<map>
map<int , int> op_2;
void init(){
op_2[1] = 4;
op_2[2] = 5;
op_2[3] = 6;
op_2[4] = 1;
op_2[5] = 2;
op_2[6] = 3;
}
int test_10_2(){
init();
int n,m;
scanf("%d%d",&n,&m);
for(int i = 0;i < m;i++){
int x,y;
scanf("%d%d",&x,&y);
conflict[x][y] = true;//冲突数标记 !!!
conflict[y][x] = true;
}
for(int j = 1;j <= 6;j++){
dp[0][j] = 1;
}
//滚动dp[cur][]...... ........
int cur = 0;
for(int level = 2;level <= n;level++){
cur = 1 - cur;
//尝试将6个面放在当前一层朝上的方向
for(int j = 1;j <= 6;j++){
dp[cur][j] = 0;
//将与op_2[j]冲突的上一层格子里面的数累加起来
for(int i = 1;i <= 6;i++){
if(conflict[op_2[j]][i])continue;//冲突不可取,跳过
dp[cur][j] = (dp[cur][j] + dp[1-cur][i]) % mod;
}
}
}
long long sum = 0;
for(int k = 1;k <= 6;k++){
sum = (sum + dp[cur][k]) % mod;
}
//快速幂求4的n次方
long long ans = 1;
long long temp = 4;
long long p = n;
while(!p){
if(p & 1 == 1)ans = (ans * temp) % mod;//奇数次幂ans先多乘一次底数4
temp = (temp*temp)%mod;
p >>= 1; //p = p/2
}
printf("%d\n",16*(sum * ans) % mod);
return 0;
}
法三:矩阵乘法:
~引用
上述动态分析方法为什么会出现超时的情况呢?
在上面dp数组动态变化的时候,递推次数过多,时间复杂度太大。
这时我们可以通过引入矩阵: 实现 “ 加法 ” 变 “ 乘法 ” 。
当“加法”转换为“乘法”后,快速幂即可得出方案数(未考虑转面情况)。
构建计算工具:
1、初始化数组:当垒第一个骰子的时候朝上的面可以取任何一个数字。
数组ans
2、在动态规划中,dp [ k ] [ j ] = ∑dp [ k - 1 ] [ i ](其中j与op[i]不冲突)
temp矩阵作用:实现 选择 垒骰子功能。
矩阵temp
3、ans数组:temp矩阵n-1 * 初始化数组 = ans数组
ans数组的值类似于动态规划中的dp[n]。
举例:图中模拟的是2数字与3数字互斥,骰子数量为2的情况。
解题路径:
①、初始化数组。
②、构建temp矩阵。
③、计算tempn-1 * 初始化数组,得到ans数组。
④、将ans数组累加得到sum。
⑤、4的快速幂得到4n。
⑥、输出结果:sum * 4n。
//矩阵
#include<bits/stdc++.h>
#define ll long long
const ll MOD = 1e9+7;
using namespace std;
ll op[7], n, m, sum=0;
bool hc[7][7]={false};
struct M{
ll a[7][7];
ll b[7];
};
void init(void)
{
op[1]=4;
op[4]=1;
op[2]=5;
op[5]=2;
op[3]=6;
op[6]=3;
}
M cf(M a, M b)
{
M c;
memset(c.a, 0, sizeof(c.a));
for(int i=1; i<7; i++)
for(int j=1; j<7; j++)
for(int k=1; k<7; k++)
c.a[i][j]=(c.a[i][j]+(a.a[i][k]*b.a[k][j])%MOD)%MOD;
return c;
}
M cf1(M a, M dw)
{
M ans;
for(int i=1; i<7; i++)
ans.b[i]=0;
for(int i=1; i<7; i++)
for(int j=1; j<7; j++)
ans.b[i]=(ans.b[i]+(a.a[i][j]*dw.b[j])%MOD)%MOD;
return ans;
}
int main()
{
init();
cin>>n>>m;
M temp, ans;
for(int i=1; i<7; i++)
for(int j=1; j<7; j++)
temp.a[i][j]=1;
for(int i=0; i<m; i++)
{
int a, b;
cin>>a>>b;
temp.a[op[a]][b]=0; // 矩阵下标统一为朝上数字
temp.a[op[b]][a]=0; // temp.a[x][y]:x与y表示两个骰子朝上的数字
}
for(int i=1; i<7; i++)
{
for(int j=1; j<7; j++)
cout<<temp.a[i][j]<<" ";
cout<<endl;
}*/
// M快速幂
ll p=n-1;
// 初始化
for(int i=1; i<7; i++)
ans.b[i]=1;
while(p!=0)
{
if(p&1) ans=cf1(temp,ans); //矩阵 * 列
temp=cf(temp,temp);
p>>=1;
}
for(int i=1; i<7; i++)
sum = (sum + ans.b[i])%MOD;
// 4快速幂
p=n;
ll a=0;
ll temp1=4;
while(p!=0)
{
if(p&1) a=(temp1+a)%MOD;
temp1=(temp1*temp1)%MOD;
p>>=1;
}
cout<<(sum*a)%MOD;
return 0;
}
2015年总结
01 隔行变色 (小学题)
02 立方尾不变 (字符串或者余数 枚举+check)
03 三羊献瑞 (枚举未知数 进位猜测 + 做题前观察!)
04 格子中输出 (代码填空题 %s对应两个参数 ,宽度,内容 )
05 串逐位和 典型的二分递归
06 奇妙的数字 (i2s整型–>字符串 + set去重 ss.size()判断 )(法二:先计算缩小枚举范围 + check)
07 加法变乘法 (简单模拟 - 前后变化关系等式) (复杂 枚举2个号的位置)
08 饮料换购 (模拟输出)
09 打印大X (图形输出 - 找规律模拟)
10 垒骰子 ( 模拟枚举 | dp |矩阵乘法 – 最难 【骰子面的对应关系 + 递归参数的选取】!!! )
int main() {
test_10_2();
return 0;
}