这次校内练习一共有6道题目,安排了一天时间来做。
虽然都做出来了,但是因为本身题目都不大难,而且自己也做了老长时间,感觉还是差了好多,尤其是编码的准确度方面。还有就是对题意的理解还是有点慢,这个也是个问题。
下面就简单说一下我对于这六道题目的解法,不一定是最好的,如果有更好的欢迎指出。
A题 放苹果
题目描述
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
输入
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
输出
对输入的每组数据M和N,用一行输出相应的K。
样例输入
1 7 3
样例输出
8
这个题我纠结了好长时间,也没想到什么好办法。也就是穷举法来做。穷举的时候,要考虑去重,这个操作起来就比较复杂了。一开始我想的是穷举每个盘子的可能情况,然后用数组保存这些情况,下次再求的时候判断是不是已经存在了。用STL的set很容易做到判断是否重复,但是很明显超时的。后来想到可以依次从第一个盘子开始枚举,而其他n-1个盘子为了避免重复,必然要小于等于他,就构造了下面所示的这个递归程序了。跑了4ms,有点慢,感觉是这个算法不是理想的,但是别的也真想不出来了。(p.s.我一开始用枚举所有组合然后去重写了个龟速版,把所有答案都打出来了。事实是这个题数据量太小,直接打表也是能过的)
我的解答如下:
1 #include<cstdio> 2 using namespace std; 3 int cnt; 4 int sum; 5 void g(int m, int n, int s) 6 { 7 if (n == 0) 8 { 9 if (s == sum) 10 cnt++; 11 return; 12 } 13 for (int i = m; i >= 0; i--) 14 { 15 g(i, n - 1, s + i); 16 } 17 } 18 int solve(int m,int n) 19 { 20 cnt = 0; 21 sum = m; 22 g(m, n, 0); 23 return cnt; 24 } 25 int main() 26 { 27 int a, b, t; 28 scanf("%d", &t); 29 while (t--) 30 { 31 scanf("%d %d", &a, &b); 32 printf("%d\n", solve(a, b)); 33 } 34 return 0; 35 }
B题 Optimal Parking
题目描述
When shopping on Long Street, Michael usually parks his car at some random location, and then walks to the stores he needs. Can you help Michael choose a place to park which minimises the distance he needs to walk on his shopping round? Long Street is a straight line, where all positions are integer. You pay for parking in a specific slot, which is an integer position on Long Street. Michael does not want to pay for more than one parking though. He is very strong, and does not mind carrying all the bags around.
输入
The first line of input gives the number of test cases, 1 ≤ t ≤ 100. There are two lines for each test case. The first gives the number of stores Michael wants to visit, 1 ≤ n ≤ 20, and the second gives their n integer positions on Long Street, 0 ≤ xi ≤ 99.
输出
Output for each test case a line with the minimal distance Michael must walk given optimal parking.
样例输入
2 4 24 13 89 37 6 7 30 41 14 39 42
样例输出
152 70
这个题也是有点坑的,一开始题目意思理解错了。我以为去完一个店就得吧东西放回车上,结果跟样例怎么都对不上。蛋疼了好长时间才发现,这货居然能带着重量级的东西直接去下一家店。不过我觉得就算拿得下拿一大堆别的东西去店里也不大好吧,寄存也不一定放得下。。。扯远了。所以说这个题坑点在于和常识不一致。理解了题意,只要找到最大值和最小值就可以了。我用了个排序,$O(nlogn)$的时间,其实用两趟遍历找最大和最小的$O(n)$是更好的,STL好像也有相关函数吧。不过数据量不大,无所谓了。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 int a[105]; 5 int main() 6 { 7 int T; 8 scanf("%d", &T); 9 while (T--) 10 { 11 int n; 12 scanf("%d", &n); 13 int i, j; 14 for (i = 0; i < n; i++) 15 { 16 scanf("%d", &a[i]); 17 } 18 sort(a, a + n); 19 printf("%d\n", (a[n - 1] - a[0]) * 2); 20 } 21 return 0; 22 }
C题 Cow Multiplication
题目描述
Bessie is tired of multiplying pairs of numbers the usual way, so she invented her own style of multiplication. In her style, $A×B$ is equal to the sum of all possible pairwise products between the digits of $A$ and $B$. For example, the product $123×45$ is equal to $1×4 + 1×5 + 2×4 + 2×5 + 3×4 + 3×5 = 54$. Given two integers $A$ and $B$ ($1 ≤ A, B ≤ 1,000,000,000$), determine $A×B$ in Bessie‘s style of multiplication.
输入
The first line of input gives the number of test cases, $1 ≤ t ≤ 100$. Each test case contains two integers A and B.
输出
Output for each test case a line with the $A×B$ in Bessie‘s style of multiplication.
样例输入
2 123 45 1 12
样例输出
54 3
这道题是全场最水,直接把每一位拆开相乘加一下就行。直接用字符串做,非常简单。
1 #include<cstdio> 2 using namespace std; 3 char str1[100], str2[100]; 4 int main() 5 { 6 int T; 7 scanf("%d", &T); 8 while (T--) 9 { 10 long long int sum = 0; 11 scanf("%s %s", str1, str2); 12 for(int i=0;str1[i]!=0;i++) 13 for (int j = 0; str2[j] != 0; j++) 14 { 15 //printf("%d * %d\n", str1[i] - ‘0‘, str2[j] - ‘0‘); 16 sum += (str1[i] - ‘0‘)*(str2[j] - ‘0‘); 17 } 18 printf("%lld\n", sum); 19 } 20 return 0; 21 }
D题 求高精度幂
题目描述
对数值很大、精度很高的数进行高精度计算是一类十分常见的问题。比如,对国债进行计算就是属于这类问题。
现在要你解决的问题是:对一个实数$R$( $0.0 < R < 99.999$ ),要求写程序精确计算 $R$ 的 $n$ 次方($R^n$),其中$n $是整数并且 $0<n≤25$。
输入
输入包括多组 $R$ 和 $n$。 $R$ 的值占第 1 到第 6 列,$n$ 的值占第 8 和第 9 列。
输出
对于每组输入,要求输出一行,该行包含精确的 $R$ 的 $n$ 次方。输出需要去掉前导的 0 后不要的 0 。如果输出是整数,不要输出小数点。
样例输入
95.123 12 0.4321 20 5.1234 15 6.7592 9 98.999 10 1.0100 12
样例输出
548815620517731830194541.899025343415715973535967221869852721 .00000005148554641076956121994511276767154838481760200726351203835429763013462401 43992025569.928573701266488041146654993318703707511666295476720493953024 29448126.764121021618164430206909037173276672 90429072743629540498.107596019456651774561044010001 1.126825030131969720661201
这个题是浮点高精度,其实是在整数高精度的基础上做就可以了。首先将小数点去掉,看成高精度整数相乘,然后根据小数点的位置($p$个$n$位小数相乘,小数位有$pn$位,包括末尾的零),补上小数点和去除前导零和末尾零就行了。就是写起来非常麻烦。有用Java过的,只要400多个Byte的代码。顺便提一下求幂可以用快速幂算法,$O(logn)$的时间。
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 string add(string n1, string n2) 5 { 6 string t, ans; 7 int i, j; 8 if (n1.length() > n2.length()) 9 { 10 t.append(n1.length() - n2.length(), ‘0‘); 11 n2 = t + n2; 12 } 13 else if (n1.length() < n2.length()) 14 { 15 t.append(n2.length() - n1.length(), ‘0‘); 16 n1 = t + n1; 17 } 18 t.clear(); 19 int carry = 0; 20 for (i = n1.length() - 1; i >= 0; i--) 21 { 22 t = (((n1[i] - ‘0‘) + (n2[i] - ‘0‘) + carry) % 10) + ‘0‘; 23 carry = ((n1[i] - ‘0‘) + (n2[i] - ‘0‘) + carry) / 10; 24 ans = t + ans; 25 } 26 if (carry) ans = ‘1‘ + ans; 27 return ans; 28 } 29 string imul(string a, string b) 30 { 31 string tmp, t; 32 string sum = "0"; 33 if (a.length() < b.length()) 34 swap(a, b); 35 int carry = 0; 36 int offset = 0; 37 for (int i = b.length() - 1; i >= 0; i--) 38 { 39 tmp.clear(); 40 carry = 0; 41 for (int j = a.length() - 1; j >= 0; j--) 42 { 43 t = (((b[i] - ‘0‘)*(a[j] - ‘0‘) + carry) % 10) + ‘0‘; 44 carry = ((b[i] - ‘0‘)*(a[j] - ‘0‘) + carry) / 10; 45 tmp = t + tmp; 46 } 47 if (carry > 0) 48 { 49 t = carry + ‘0‘; 50 tmp = t + tmp; 51 } 52 if (offset)tmp.append(offset, ‘0‘); 53 offset++; 54 sum = add(sum, tmp); 55 } 56 return sum; 57 } 58 string power(string base, int n) 59 { 60 string result = "1"; 61 while (n != 0) 62 { 63 if (n & 1) result = imul(result, base); 64 base = imul(base, base); 65 n /= 2; 66 } 67 return result; 68 } 69 string solve(string base, int n) 70 { 71 int dotp = base.find(‘.‘); 72 int fltcnt = base.size() - dotp - 1; 73 string base2; 74 int i; 75 for (i = 0; i < base.length(); i++) 76 if (base[i] != ‘.‘)base2.push_back(base[i]); 77 string powresult = power(base2, n), ansi, ansf, ans; 78 bool flag = false; 79 for (i = 0; i < powresult.length() - fltcnt*n; i++) 80 { 81 if (powresult[i] != ‘0‘ || flag) 82 { 83 ansi.push_back(powresult[i]); 84 flag = true; 85 } 86 } 87 ansi.push_back(‘.‘); 88 flag = false; 89 for (i = 0; i < n*fltcnt; i++) 90 { 91 if (powresult[powresult.length() - 1 - i] != ‘0‘ || flag) 92 { 93 ansf = powresult[powresult.length() - 1 - i] + ansf; 94 flag = true; 95 } 96 } 97 return ansi + ansf; 98 } 99 int main() 100 { 101 ios::sync_with_stdio(false); 102 string s; 103 int n; 104 while (cin >> s) 105 { 106 cin >> n; 107 cout << solve(s, n) << endl; 108 } 109 return 0; 110 }
E题
题目描述
You have just moved from Waterloo to a big city. The people here speak an incomprehensible dialect of a foreign language. Fortunately, you have a dictionary to help you understand them.
输入
Input consists of up to 100,000 dictionary entries, followed by a blank line, followed by a message of up to 100,000 words. Each dictionary entry is a line containing an English word, followed by a space and a foreign language word. No foreign word appears more than once in the dictionary. The message is a sequence of words in the foreign language, one word on each line. Each word in the input is a sequence of at most 10 lowercase letters.
输出
Output is the message translated to English, one word per line. Foreign words not in the dictionary should be translated as "eh".
样例输入
dog ogday cat atcay pig igpay froot ootfray loops oopslay atcay ittenkay oopslay
样例输出
cat eh loops
提示
Huge input and output,scanf and printf are recommended.
这个题是求两个字符串的对应。最容易想到的是使用STL的map容器,但是很明显,这题这样做会超时。map底层是红黑树实现的,其worst-case时间复杂度虽然也能保证为$O(logn)$,但是这个题数据量很大,而且STL本身也很慢。考虑$O(1)$的数据结构,可以使用哈希表和前缀树。我这里使用前缀树做的。其实这道题之前在vjudge也刷过,用的就是哈希表。哈希表最常见的方法是拉链法,我当时用的是STL的vector来代替链表。那边的服务器应该是开了O2优化的,435ms完美AC。但是学校的服务器没开O2,STL就是各种坑点了,用STL实现的哈希表就是过不了。我特地把我这个AC掉的用vjudge那边的又跑了一遍,需要200多ms,学校的服务器只需要100ms。可惜学校的服务器用那个vector的哈希表在没开O2的情况3000ms都不够,真是无语。
后来在做前缀树的时候,用string类解析输入、传参,和动态内存分配依然过不了。最后不得已,用了传统的C-style的字符串和开大数组的方法,才过了。另外这题还有一个坑点,就是输入处理。gets()读取到的空行第一个字符是‘\n‘。用这个作为标志就行了。
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstring> 4 #include<string> 5 using namespace std; 6 struct node 7 { 8 bool isword; 9 char translated[15]; 10 node * childs[26]; 11 node() 12 { 13 isword = false; 14 } 15 }; 16 node * trie; 17 size_t top; 18 node pool[150005]; 19 inline node * newnode() 20 { 21 return &pool[top++]; 22 } 23 void addword(const char * src , char * dst) 24 { 25 node * pn = trie; 26 int i; 27 for (i = 0; src[i] != 0; i++) 28 if (pn->childs[src[i] - ‘a‘]) 29 { 30 pn = pn->childs[src[i] - ‘a‘]; 31 } 32 else 33 { 34 pn->childs[src[i] - ‘a‘] = newnode(); 35 pn = pn->childs[src[i] - ‘a‘]; 36 } 37 pn->isword = true; 38 strcpy(pn->translated, dst); 39 } 40 char * findword(const char * src) 41 { 42 node * pn = trie; 43 int i; 44 for (i = 0; src[i] != 0; i++) 45 if (pn->childs[src[i] - ‘a‘] != NULL) 46 pn = pn->childs[src[i] - ‘a‘]; 47 else 48 return "eh"; 49 return pn->translated; 50 } 51 int main() 52 { 53 char buf[256]; 54 char dst[15], src[15]; 55 trie = newnode(); 56 for (;;) 57 { 58 fgets(buf, 255, stdin); 59 if (buf[0] == ‘\n‘) 60 break; 61 sscanf(buf, "%s %s", dst, src); 62 addword(src, dst); 63 } 64 while (scanf("%s", buf) != EOF) 65 puts(findword(buf)); 66 return 0; 67 }
之前的哈希表实现也附上吧
1 #include<iostream> 2 #include<vector> 3 #include<map> 4 #include<string> 5 using namespace std; 6 vector<pair<string,string> > hashtbl[99991]; 7 unsigned hashfunc(string & s) 8 { 9 int i; 10 unsigned hshval=0; 11 for(i=0;i<s.length();i++) 12 hshval=(hshval<<5)+s[i]; 13 return hshval%99991; 14 } 15 int main() { 16 std::ios::sync_with_stdio(false); 17 string tmpline; 18 for(;;) 19 { 20 getline(cin,tmpline); 21 if(tmpline=="")break; 22 int p=tmpline.find(‘ ‘); 23 pair<string,string> pr; 24 pr.first=tmpline.substr(0,p); 25 pr.second=tmpline.substr(p+1,tmpline.length()-p-1); 26 hashtbl[hashfunc(pr.second)].push_back(pr); 27 } 28 while(cin>>tmpline) 29 { 30 int i; 31 bool flag=false; 32 unsigned hsv=hashfunc(tmpline); 33 for(i=0;i<hashtbl[hsv].size();i++) 34 { 35 if(hashtbl[hsv][i].second==tmpline) 36 { 37 cout<<hashtbl[hsv][i].first<<endl; 38 flag=true; 39 } 40 } 41 if(!flag)cout<<"eh"<<endl; 42 } 43 return 0; 44 }
F题 Pots
题目描述
You are
given two pots, having the volume of $A$ and $B$ liters respectively. The following
operations can be performed:
FILL($i$) fill the pot i ($1 ≤ i ≤ 2$) from the tap;
DROP($i$) empty the pot $i$ to the drain;
POUR($i$,$j$) pour from pot $i$ to pot $j$; after this
operation either the pot $j$ is full (and there may be some water left in the pot
$i$), or the pot $i$ is empty (and all its contents have been moved to the pot $j$).
Write a
program to find the shortest possible sequence of these operations that will
yield exactly $C$ liters of water in one of the pots.
输入
On the
first and only line are the numbers $A,B$, and $C$. These are all integers in the
range from $1$ to $100$ and $C≤max(A,B)$.
输出
The first
line of the output must contain the length of the sequence of operations $K$. The
following $K$ lines must each describe one operation. If there are several
sequences of minimal length, output any one of them. If the desired result
can’t be achieved, the first and only line of the file must contain the word
‘impossible’.
样例输入
3 5 4
样例输出
6 FILL(2) POUR(2,1) DROP(1) POUR(2,1) FILL(2) POUR(2,1)
这个题是隐式图问题,之前做过杭电的一道电梯题,跟这个相类似。
两个杯子的水来回倒,或者去接水,或者去倒掉;也就是说,在每一步有四个状态的转换。而在具体的处理上,两个杯子相互倒的时候,还要考虑到A倒到B中,B是满了还是没满。因此把整个问题看作图,两个杯子的水的容量构成的二元组看作是顶点,具体的状态转换(倒掉A,倒掉B,接满A,接满B,A倒到B,B倒到A)看作是边,用BFS就可以做了。对于要追踪BFS,《算法导论》书中提到是构建搜索树的方法,增加前驱顶点的指针就可以了,这样搜索到结尾,只需要往回找,就能把整个路径输出了。
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<stack> 5 #include<set> 6 #include<string> 7 using namespace std; 8 int gp[105][105] = { 0 }; 9 struct mypair 10 { 11 int first; 12 int second; 13 string command; 14 mypair * father; 15 mypair(int f, int s, char * c,mypair * fh) 16 { 17 first = f; 18 second = s; 19 command = c; 20 father = fh; 21 } 22 mypair() 23 { 24 first = second = 0; 25 father = NULL; 26 } 27 }; 28 mypair * pt; 29 mypair pool[500]; 30 size_t ptop; 31 queue<mypair *> qu; 32 int a, b, c; 33 inline mypair * newmypair() 34 { 35 return &pool[ptop++]; 36 } 37 inline void update(int x, int y, char * s) 38 { 39 if (gp[x][y] == 0) 40 { 41 mypair * mp = newmypair(); 42 mp->first = x; 43 mp->second = y; 44 mp->command = s; 45 mp->father = pt; 46 qu.push(mp); 47 gp[x][y] = gp[pt->first][pt->second] + 1; 48 } 49 } 50 void print(mypair * p) 51 { 52 stack<string> st; 53 while (p != NULL) 54 { 55 st.push(p->command); 56 p = p->father; 57 } 58 while (!st.empty()) 59 { 60 cout << st.top() << endl; 61 st.pop(); 62 } 63 } 64 int main() 65 { 66 scanf("%d%d%d", &a, &b, &c); 67 pt = newmypair(); 68 pt->first = 0; 69 pt->second = 0; 70 pt->father = NULL; 71 qu.push(pt); 72 int t = 1; 73 gp[0][0] = 1; 74 while (!qu.empty()) 75 { 76 pt = qu.front(); 77 if (pt->first == c || pt->second == c) 78 { 79 cout << gp[pt->first][pt->second] - 1; 80 print(pt); 81 goto FINALLY; 82 } 83 update(a, pt->second, "FILL(1)"); 84 update(pt->first, b, "FILL(2)"); 85 update(0, pt->second, "DROP(1)"); 86 update(pt->first, 0, "DROP(2)"); 87 if (pt->first + pt->second > b) 88 update(pt->first - (b - pt->second), b, "POUR(1,2)"); 89 else 90 update(0, pt->first + pt->second, "POUR(1,2)"); 91 if (pt->first + pt->second > a) 92 update(a, pt->second - (a - pt->first), "POUR(2,1)"); 93 else 94 update(pt->first + pt->second, 0, "POUR(2,1)"); 95 qu.pop(); 96 } 97 cout << "impossible" << endl; 98 FINALLY: 99 return 0; 100 }