奇怪的分式
上小学的时候,小明经常自己发明新算法。一次,老师出的题目是:
1/4 乘以 8/5、
小明居然把分子拼接在一起,分母拼接在一起,答案是:18/45 (参见图1.png)
老师刚想批评他,转念一想,这个答案凑巧也对啊,真是见鬼!
对于分子、分母都是 1~9 中的一位数的情况,还有哪些算式可以这样计算呢?
请写出所有不同算式的个数(包括题中举例的)。
显然,交换分子分母后,例如:4/1 乘以 5/8 是满足要求的,这算做不同的算式。
但对于分子分母相同的情况,2/2 乘以 3/3 这样的类型太多了,不在计数之列!
注意:答案是个整数(考虑对称性,肯定是偶数)。请通过浏览器提交。不要书写多余的内容。
思路:
穷举算法:用4个变量来表示分子和分母,分式可表示为a/b×c/d=ac/bd,只要对a,b,c,d的所有情况进行穷举,穷举范围1~9,穷举过程中对分式是否成立进行判断,成立计数器加1,最后输出结果。穷举过程中还有两钟情况需要注意,即a!=b,c!=d。
在计算分式时不能直接计算,因为分式的计算结果是浮点数,浮点数在比较是否相等时是不准确的,如0.1+0.2==0.3这个条件看起来是成立的,而实际结果为false。因此要化分数运算为整数运算,分式a/b×c/d=ac/bd要转换为a×c×bd=b×d×ac。
#include <iostream> using namespace std; int ans; int gcd(int a, int b) { if (b == 0) return a; return gcd(b, a % b); } int main(int argc, const char *argv[]) { cout << gcd(12, 16) << endl; for (int a = 1; a < 10; ++a) { for (int b = 1; b < 10; ++b) { if (b == a)continue; for (int c = 1; c < 10; ++c) { for (int d = 1; d < 10; ++d) { if (c == d)continue; int g1 = gcd(a * c, b * d); int g2 = gcd(a * 10 + c, b * 10 + d); if (a * c / g1 == (a * 10 + c) / g2 && b * d / g1 == (b * 10 + d) / g2) { printf("%d %d %d %d\n", a, b, c, d); ans++; } } } } } cout << ans << endl; return 0; }
答案:14
六角填数
如图所示六角形中,填入1~12的数字。
使得每条直线上的数字之和都相同。
图中,已经替你填好了3个数字,请你计算星号位置所代表的数字是多少?
思路:
1.用回溯法就可以把每个位置的数就可以穷举一次。遇到合适的就结束输出。
2.源代码中用vis数组来标记哪个数字已经被使用过,dfs中用n来表示位置,i表示要赋的值。
3.关键要点就是写好函数中刚开始需要的值和回溯的出口。函数里面的for记得要枚举一个标记后再重新成为未标记。
4.从上到下依次标记为1-12。
代码
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<string> #include<string.h> #include<math.h> #include<map> #include<vector> #include<algorithm> #include<stack> #include<queue> using namespace std; #define MAX 0x3f3f3f3f #define MIN -0x3f3f3f3f int ans[13]; bool vis[13]; void dfs(int n) { if(n==1||n==2||n==12) { dfs(n+1); return; } if(n>=13) { int t[6]; t[0]=ans[1]+ans[3]+ans[6]+ans[8]; t[1]=ans[1]+ans[4]+ans[7]+ans[11]; t[2]=ans[8]+ans[9]+ans[10]+ans[11]; t[3]=ans[2]+ans[3]+ans[4]+ans[5]; t[4]=ans[2]+ans[6]+ans[9]+ans[12]; t[5]=ans[5]+ans[7]+ans[10]+ans[12]; for(int i=1;i<=5;i++) { if(t[i]!=t[i-1]) { return; } } printf("%d\n",ans[6]); return; } for(int i=1;i<=12;i++) { if(!vis[i]) { vis[i]=true; ans[n]=i; dfs(n+1); vis[i]=false; } } } int main(int argc, char *argv[]) { memset(ans,0,sizeof(ans)); ans[1]=1; ans[2]=8; ans[12]=3; vis[1]=vis[3]=vis[8]=1; dfs(1); return 0; }
蚂蚁感冒
长100厘米的细长直杆子上有n只蚂蚁。它们的头有的朝左,有的朝右。
每只蚂蚁都只能沿着杆子向前爬,速度是1厘米/秒。
当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。
这些蚂蚁中,有1只蚂蚁感冒了。并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。
请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。
【数据格式】
第一行输入一个整数n (1 < n < 50), 表示蚂蚁的总数。
接着的一行是n个用空格分开的整数 Xi (-100 < Xi < 100), Xi的绝对值,表示蚂蚁离开杆子左边端点的距离。
正值表示头朝右,负值表示头朝左,数据中不会出现0值,也不会出现两只蚂蚁占用同一位置。
其中,第一个数据代表的蚂蚁感冒了。
要求输出1个整数,表示最后感冒蚂蚁的数目。
例如,输入:
1. 3 2. 5 -2 8
程序应输出:
1
再例如,输入:
1. 5 2. -10 8 -20 12 25
程序应输出:
3
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。
代码
#include <iostream> using namespace std; int main(int argc, const char * argv[]) { int n; scanf("%d",&n); int arr[n]; for (int i = 0; i < n; ++i) { scanf("%d",&arr[i]); } int x = arr[0]; if(x>0){//向右 int ans=1; for (int i = 0; i < n; ++i) { if(arr[i]<0&&-arr[i]>x)//从右向左 ans++; } if(ans!=1)//有从右到左 for (int i = 0; i < n; ++i) { if(arr[i]>0&&arr[i]<x)//从右向左 ans++; } printf("%d\n",ans); } if(x<0){//向左 // 左侧从左到右的 int ans=1; for (int i = 0; i < n; ++i) { if(arr[i]>0&&arr[i]<-x) ans++; } if(ans!=1) for (int i = 0; i < n; ++i) { if(arr[i]<0&&-arr[i]>-x) ans++; } printf("%d\n",ans); } return 0; }
地宫取宝
X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。
地宫的入口在左上角,出口在右下角。
小明被带到地宫的入口,国王要求他只能向右或向下行走。
走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。
当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。
请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。
【数据格式】
输入一行3个整数,用空格分开:n m k (1<=n,m<=50, 1<=k<=12)
接下来有 n 行数据,每行有 m 个整数 Ci (0<=Ci<=12)代表这个格子上的宝物的价值
要求输出一个整数,表示正好取k个宝贝的行动方案数。该数字可能很大,输出它对 1000000007 取模的结果。
例如,输入:
1. 2 2 2 2. 1 2 3. 2 1
程序应该输出:
2
再例如,输入:
1. 2 3 2 2. 1 2 3 3. 2 1 5
程序应该输出:
14
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。
代码
#include <iostream> #include <cstring> using namespace std; const int MOD = 1000000007; int n, m, k; int data[50][50]; long long ans; long long cache[50][50][14][13]; void dfs(int x, int y, int max, int cnt) { if (x == n || y == m || cnt > k) return; int cur = data[x][y]; if (x == n - 1 && y == m - 1)//已经面临最后一个格子 { if (cnt == k || (cnt == k - 1 && cur > max)) { ans++; if (ans > MOD) ans %= MOD; } } if (cur > max) {//可以取这个物品 dfs(x, y + 1, cur, cnt + 1); dfs(x + 1, y, cur, cnt + 1); } //对于价值较小,或者价值大但不去这个物品的情况如下 dfs(x, y + 1, max, cnt); dfs(x + 1, y, max, cnt); } long long dfs2(int x, int y, int max, int cnt) { // 查缓存 if (cache[x][y][max+1][cnt] != -1) return cache[x][y][max+1][cnt]; long long ans = 0; if (x == n || y == m || cnt > k) return 0; int cur = data[x][y]; if (x == n - 1 && y == m - 1)//已经面临最后一个格子 { if (cnt == k || (cnt == k - 1 && cur > max)) { ans++; if (ans > MOD) ans %= MOD; } return ans; } if (cur > max) {//可以取这个物品 ans += dfs2(x, y + 1, cur, cnt + 1); ans += dfs2(x + 1, y, cur, cnt + 1); } //对于价值较小,或者价值大但不去这个物品的情况如下 ans += dfs2(x, y + 1, max, cnt); ans += dfs2(x + 1, y, max, cnt); cache[x][y][max+1][cnt]=ans % MOD; return cache[x][y][max+1][cnt]; } int main(int argc, const char *argv[]) { scanf("%d %d %d", &n, &m, &k); for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { scanf("%d", &data[i][j]); } } // dfs(0, 0, -1, 0);//第一个点的价值可能是0 // printf("%d\n", ans); memset(cache,-1, sizeof(cache)); printf("%lli\n", dfs2(0, 0, -1, 0)); return 0; }
小朋友排队
n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。
每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。
如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。
当要求某个小朋友第k次交换时,他的不高兴程度增加k。
请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。
如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
【数据格式】
输入的第一行包含一个整数n,表示小朋友的个数。
第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。
例如,输入:
1. 3 2. 3 2 1
程序应该输出:
9
【样例说明】
首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。
【数据规模与约定】
对于10%的数据, 1<=n<=10;
对于30%的数据, 1<=n<=1000;
对于50%的数据, 1<=n<=10000;
对于100%的数据,1<=n<=100000,0<=Hi<=1000000。
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。
#include <iostream> #include <cstring> #include <cstdio> using namespace std; int lowbit(int n) { return n - (n & (n - 1)); } /** * 原始数组的i位置增加v后,更新c数组 * @param i * @param v * @param c */ void updata(int n, int i, int v, int c[]) { for (int k = i; k <= n; k += lowbit(k)) { c[k] += v; } } int getSum(int c[], int i) { int sum = 0; for (int k = i; k >= 1; k -= lowbit(k)) { sum += c[k]; } return sum; } int h[100000]; long long cnt[100000];//记录每个孩子的交换次数 int c[1000000 + 1]; int main(int argc, const char *argv[]) { // int arr[]={1,2,3,4,5,6,7,8}; // int c[9]; // memset(c,0, sizeof(c)); // for (int i = 0; i < 8; ++i) { // updata(9,i+1,arr[i],c); // } // cout<<getSum(c,5)<<endl; // cout<<getSum(c,6)<<endl; // cout<<getSum(c,7)-getSum(c,4)<<endl; // freopen("/Users/zhengwei/Desktop/其他/input8 (1).txt", "r", stdin); int n; scanf("%d", &n); // memset(cnt,0,sizeof(cnt)); int maxH = -1; for (int i = 0; i < n; ++i) { scanf("%d", &h[i]); if (h[i] > maxH)maxH = h[i]; } for (int i = 0; i < n; ++i) { updata(maxH + 1, h[i] + 1, 1, c);//在响应位置计数变为1,其实就是用树状数组维护数据出现的个数 // int sum = getSum(c, h[i] + 1);//小于等于h[i]+1的数据的个数 cnt[i] += (i + 1) - sum;//得到的就是当前数据左侧比数据大的数的个数 } memset(c, 0, sizeof(c)); for (int i = n - 1; i >= 0; --i) { updata(maxH + 1, h[i] + 1, 1, c);//在响应位置计数变为1,其实就是用树状数组维护数据出现的个数 // // int sum = getSum(c, h[i] + 1);//小于等于h[i]+1的数据的个数 // int self = getSum(c,h[i]+1)-getSum(c,h[i]); // cnt[i] += sum-self;//上一步求出小于等于h的个数,扣掉自己的个数,得到的就是当前数据右侧比数据小的数的个数 cnt[i] += getSum(c, h[i]);//求出小于h[i]+1 的数据的个数 } long long ans = 0; for (int i = 0; i < n; ++i) { ans += (cnt[i] * (cnt[i] + 1) / 2); } printf("%lli\n", ans); return 0; }