您的位置:首页 > 其它

入门经典_Chap05_习题题解

2017-06-24 12:00 183 查看

前言

选择性地做了9道题目,这些题目在难度上不是很大,主要是训练熟练运用STL的知识,只要注意代码的条理性就好了

从此以后不再一道题写一篇博客了,感觉太浪费,以后一章的题就只写一篇博客,主要是核心思路和ac代码,忽视了一些细节,这样应该更好一点吧

题解如下

UVA1593_Alignment of Code

思路

输入多行文本,使每一列的单词左对齐,两列之间隔一个空格。

用一个数组记录每列的最大长度即可。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
using namespace std;
typedef long long LL;
const int maxn = 1e3 + 100;
const int INF = 0x7fffffff;

string a[maxn][200], str, stmp;
int len[maxn];
int cnt[200];

int main(){
#ifdef _LOCAL
IN;
#endif // _LOCAL

int id = 0; met(len, 0); met(cnt, 0);
while(getline(cin,str)) {
//cout << str <<endl;
stringstream ss(str);
while(ss >> stmp) {
a[id][len[id]] = stmp;
cnt[len[id]] = max(cnt[len[id]], (int)stmp.length()+1);
++len[id];
}
++id;
}

for(int i = 0; i < id; ++i) {
for(int j = 0; j < len[i]; ++j) {
cout << a[i][j];
int tl = a[i][j].size();
if(j != len[i]-1) for(int k = 0; k < cnt[j]-tl; ++k) printf(" ");
}
printf("\n");
}

return 0;
}


UVA1594_Ducci Sequence

思路

有一个序列,环形的,每次变化这个序列上任何一个数都变成与它后面那个数差的绝对值;那么这个序列最后要么会变成全0,要么会循环往复,让你判断会是哪种情况

因为题目说了每个数最多是1000,所以我这里采取的做法是将这个序列的每一个数按四位存为一个字符串然后拼接起来,再放入一个set里,要判断是否会循环只要在set里找就行了。

但是后来想了想, 直接用vector存这些数然后放进set也是可以的,还更省事些。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
using namespace std;
typedef long long LL;
const int maxn = 1e3 + 100;
const int INF = 0x7fffffff;

char ot[2][10] = {"ZERO", "LOOP"};

int n, a[maxn];
set<string> S;

int judge() {
int sum = 0;
for(int i = 0; i < n; ++i) sum += a[i];
if(sum == 0) return 1;

char str[maxn];
for(int i = 0; i < n; ++i) {
sprintf(str+i*4,"%04d", a[i]);
}
str[n*4+1] = '\0';
string tmp = str;
if(S.count(tmp)) return 2;
else {
S.insert(tmp);
int tt = a[0];
for(int i = 0; i < n-1; ++i) {
a[i] = abs(a[i]- a[i+1]);
}
a[n-1] = abs(a[n-1]-tt);
return 0;
}

}

int main(){
#ifdef _LOCAL
IN;
#endif // _LOCAL

int t; cin >> t;
while(t--) {
scanf("%d", &n); S.clear();
for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
int t = judge();
while(!t) t = judge();
printf("%s\n", ot[t-1]);
}

return 0;
}


UVA10935_ Throwing cards away I

思路

一堆扑克牌,从上到下为从1到n,你能做的操作就是把最上面一张牌拿走,然后把当前的最上面一张牌放在最下面,直到最后还剩一张牌。让你输出这个拿走牌的序列以及最后剩下的那张牌

用一个队列queue模拟过程即可,水题一道。数据范围是50,所以我这里把结果都打表出来了。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
using namespace std;
typedef long long LL;
const int maxn = 1e3 + 100;
const int INF = 0x7fffffff;

int ans[55][55];
queue<int> q;

int main(){
#ifdef _LOCAL
IN;
#endif // _LOCAL

for(int i = 1; i <= 50; ++i) {
while(!q.empty()) q.pop();
for(int j = 1; j <= i; ++j) q.push(j);

int id = 0;
while(!q.empty()) {
ans[i][id++] = q.front(); q.pop();
int t = q.front(); q.pop(); q.push(t);
}
}

int n;
while(cin >> n && n) {
printf("Discarded cards:");
for(int i = 0; i < n-1; ++i) {
if(i != 0) printf(",");
printf(" %d", ans
[i]);
}
printf("\nRemaining card: %d\n", ans
[n-1]);
}

return 0;
}


UVA10763_Foreign Exchange

思路

有一些候选人,最多500000个,他们都想从当前的位置A跳到位置B,但是他可以成功这样做的前提是有一个人会到A来同时有一个人会从B走,问你列出来的意向表里面可不可以全部实现。

用一个数组统计意向表。扫描意向表,在的那个位置+1,想去的那个位置-1,这样最后这个数组所有元素都为0说明每个位置出去的和进来的个数相等,就是YES了

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 100;
const int INF = 0x7fffffff;

int n, a, b;
int val[maxn];

int main(){
#ifdef _LOCAL
IN;
#endif // _LOCAL

while(scanf("%d", &n) == 1 && n) {
met(val, 0);
for(int i = 0; i < n; ++i) {
scanf("%d%d", &a, &b);
++val[a]; --val;
}
bool ok = 1;
for(int i = 0; i < maxn; ++i) {
if(val[i] != 0) { ok = 0; break; }
}
if(ok) printf("YES\n");
else printf("NO\n");
}

return 0;
}


UVA10391_Compound Words

思路

[b] 有一些单词,它们是按字典序排列好的(这些很重要),数量可达120000个,现在让你找出所有的组合单词,组合单词的定义是在给出的单词里可以找到两个单词拼接成这个单词。


这题好, 个人感觉这是一道特别好的题目。首先,不用说,我肯定要把所有的单词放入set里;

然后呢,我就开始对每一个单词进行判断,我们注意到,它们原来就是按字典序排好的,所以我们只要从跟这个单词首字母一样的单词开始找起就好了.

我们先看这个单词是不是此单词的前缀,如果是的话,就将后缀取出来,在set里查找有没有这个单词,如果有的话,此单词就是组合单词了。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
using namespace std;
typedef long long LL;
const int maxn = 2e5 + 100;
const int INF = 0x7fffffff;

string s[maxn], str;
int sid = 0;
set<string> m, ans;

int main(){
#ifdef _LOCAL
IN;
#endif // _LOCAL

while(cin >> str) s[sid++] = str, m.insert(str);

for(int i = 0; i < sid; ++i) {
string st = s[i];
int j = i-1;
while(j >= 0 && s[j][0] == st[0]) {
int tid = st.find(s[j]);
if(tid == 0) {
string tmp = st.substr(s[j].size());
if(m.count(tmp)) ans.insert(st);
}
--j;
}
}

for(set<string>::iterator it = ans.begin(); it != ans.end(); ++it) {
cout << (*it) <<endl;
}

return 0;
}


UVA1595_Symmetry

思路

给你一些二维坐标,让你判断这些坐标会不会沿某个竖线对称

按x值排序,找到中间坐标的x值,或者偶数时两个中间坐标x的平均值,然后先判断左边的点和右边的点的个数是不是一样的,不一样的肯定不满足了,个数一样的话看看两个x对称的坐标的y值是不是相等的,有不相等的就不满足了。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <utility>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int maxn = 2e6 + 100;
const int INF = 0x7fffffff;

int n, a[maxn], b;
set<PII> s;

int main(){
#ifdef _LOCAL
IN;
#endif // _LOCAL

int t; cin >> t;
while(t--) {
scanf("%d", &n); s.clear();
for(int i = 0; i < n; ++i) {
scanf("%d%d", &a[i], &b);
s.insert(make_pair(a[i], b));
}

set<PII>::iterator it = s.begin();
sort(a, a+n); bool ans = 1;

int mid = a[n/2];
int L = 0, R = 0;
for(int i = 0; i <= n && a[i] != mid; ++i) ++L;
for(int i = n-1; i >= 0 && a[i] != mid; --i) ++R;
if(L != R) ans = 0;
else {
for(int i = 0; i < L && it != s.end(); ++i, ++it) {
if(!s.count(make_pair(2*mid-it->first,it->second))){
ans = 0; break;
}
}
}

if(!ans) {
if(!(n&0)) {
ans = 1;
double mid = (a[n/2] + a[n/2-1])/2.0; int i = 0;
for(it = s.begin(); i < n/2 && it != s.end(); ++i, ++it) {
if(!s.count(make_pair(2*mid-it->first,it->second))){
ans = 0; break;
}
}
}
}

if(ans) printf("YES\n");
else printf("NO\n");
/*for(set<PII>::iterator it = s.begin(); it!= s.end(); ++it) {
cout << it->first << " " <<it->second <<endl;
}*/
}
return 0;
}


UVA12100_Printer Queue

思路

有一个打印机任务序列1到n,每一个任务都有一个优先级,如果当这个任务要执行的时候,候选序列里还有比此序列优先级更高的,就将此序列放在最后面。

队列模拟,然后判断一下优先级即可。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <utility>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int maxn = 2e6 + 100;
const int INF = 0x7fffffff;

int n, m, level[maxn];
bool vis[maxn];

bool check(int x) {
for(int i = 0; i < n; ++i) {
if(vis[i]|| i == x) continue;
if(level[i] > level[x]) return 0;
}
return 1;
}

int main(){
#ifdef _LOCAL
IN;
#endif // _LOCAL

int t; cin >> t;
while(t--) {
queue<int> q;
scanf("%d%d", &n, &m); met(vis, 0);
for(int i = 0; i < n; ++i) scanf("%d", &level[i]);
for(int i = 0; i < n; ++i) q.push(i);

int ans = 0;
while(!q.empty()) {
int t = q.front();q.pop();
if(check(t)) {
++ans, vis[t] = 1;
if(t == m) break;
}
else q.push(t);
}
printf("%d\n", ans);
}

return 0;
}


UVA230_Borrowers

思路

这题有点复杂,借书,还书,上架的图书管理系统,还书的时候要换作者名字的字典序排列,作者名字相等时要按书名字典序排列

自定义一个book的结构体,重载好<符号以便排序,然后用一个map维护每一个书的状态(以书名为键值),总共有在架,借出,已还但未上架三种状态。这题的关键是这三种状态。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <utility>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int maxn = 3e3 + 100;
const int INF = 0x7fffffff;

string s, str;
struct node {
string name;
string author;
bool operator < (const node& b) const {
if(author == b.author) return name < b.name;
return author < b.author;
}
}book[maxn];
map<string, int> val;

int main(){
#ifdef _LOCAL
IN;
#endif // _LOCAL

int n = 0;
while(getline(cin, s)) {
if(s == "END") break;
int pos = s.substr(1).find('"');
book
.name = s.substr(0,pos+2);

pos = s.substr(pos+3).find("by") + pos+4;
book
.author = s.substr(pos+2);

val[book
.name] = 1; ++n;
}

sort(book, book + n);
while(cin >> s) {
if(s == "BORROW") {
getline(cin, str);
val[str.substr(1)] = 0;
}
else if(s == "RETURN") {
getline(cin, str);
val[str.substr(1)] = 2;
}
else if(s == "SHELVE") {
for(int i = 0; i < n; ++i) {
if(val[book[i].name] == 2) {
cout << "Put " << book[i].name <<" ";
int pre = -1;
for(int j = i-1; j >= 0 ; --j) {
if(val[book[j].name] == 1){
pre = j; break;
}
}
if(pre == -1) cout << "first\n";
else cout << "after " << book[pre].name <<endl;
val[book[i].name] = 1;
}
}
cout << "END\n";
}
else if(s == "END") break;
}

return 0;
}


UVA12333_Revenge of Fibonacci

思路

给你一些数,不超过40位且无前导0,让你判断这些数是不是小于第100000个某个菲波那契数的前缀,如果是的话,输出下标最小的那个满足条件的菲波那契数的下标,否则输出-1。

这题有点变态。首先时间限制是10秒!!!,还没做过时间那么长的题目。

这题需要分解一下。

首先,前100000个菲波那契数是一定要求出来的,那就需要高精度的大数加法了,而且如果把这100000个数都存下来,数组不够大吧。那就只存前45位好了, 这样也够用了。这是第一步:用大数加法求出前100000个菲数并保存每一个的前45位。

第二步,求前缀所在的最小下标。字典树模板题,只要将保存的前45位插入字典树中,并用字典树维护前缀出现的最小下标即可。

个人感觉此题有两个坑点。第一个是大数加法不能使用一位一位的那种,会超时,我这里用的是一次计算8位的大数加法。第二个是插入字典树的菲波那契数不能多也不能少, 尤其不能把第100000个加进去,因为题目要求的是小于100000的,如果下标大于等于100000的要输出-1的。

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <utility>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MAX = 10;
const int maxn = 1e6 + 100;
const int INF = 0x7fffffff;

char f[maxn][50];
map<string, int> m;

struct Trie{
int val;
Trie *next[MAX];
Trie() {
val = -1;
}
} *root;

void Insert(char *s, int idx){
int n = strlen(s);
Trie *pre = root, *cur;
for (int i = 0; i < n && i <= 40; i++){
int id = s[i] - '0';
if (pre->next[id] == NULL){
//新建一个结点
cur = new Trie();
//cur->val = -1;
for (int j = 0; j < MAX; j++)cur->next[j] = NULL;
//开始插入
pre->next[id] = cur;
pre = pre->next[id];
}
else{
pre = pre->next[id];

}
if(pre->val == -1) pre->val = idx;
}
}

int Find(char *s){
int n = strlen(s);
Trie *pre = root;
for (int i = 0; i < n; i++){
int id = s[i] - '0';
if (pre->next[id] == NULL)return -1;
pre = pre->next[id];
}
return pre->val;
}

void Delete(Trie *rt){
if (NULL == rt)return;
for (int i = 0; i < MAX; i++){
if (rt->next[i] != NULL)Delete(rt->next[i]);
}
delete rt;
rt = NULL;
}

struct BigInteger {
static const int BASE = 100000000;
static const int WIDTH = 8;
vector<int> s;
BigInteger(long long num = 0) {
*this = num;    //构造函数
}

BigInteger operator = (long long num) { //赋值运算符
s.clear();
do {
s.push_back(num % BASE);
num /= BASE;
} while(num > 0);
return *this;
}

BigInteger operator + (const BigInteger& b) const {
BigInteger c;
c.s.clear();
for(int i = 0, g = 0; ; i++) {
if(g == 0 && i >= s.size() && i >= b.s.size()) break;
int x = g;
if(i < s.size()) x += s[i];
if(i < b.s.size()) x += b.s[i];
c.s.push_back(x % BASE);
g = x / BASE;
}
return c;
}

};

BigInteger a, b;
char tmp[50];

int main(){
#ifdef _LOCAL
IN;
#endif // _LOCAL

root = new Trie;
for(int i = 0; i < MAX; ++i) root->next[i] = NULL;

a = b = 1;
strcpy(f[0], "1"); strcpy(f[1], "1");
Insert(f[0], 0); Insert(f[1], 1);
for(int j = 2; j < 100000; ++j) {
if(j&1) {
a = a+b;
sprintf(f[j], "%d", a.s.back());
for(int i = a.s.size()-2; i >= 0; i--) {
if(strlen(f[j]) > 50) break;
char buf[20];
sprintf(buf, "%08d", a.s[i]);buf[8] = '\0';
strncat(f[j], buf,8);
}

}
else {
b = b+a;
sprintf(f[j], "%d", b.s.back());
for(int i = b.s.size()-2; i >= 0; i--) {
if(strlen(f[j]) > 50) break;
char buf[20];
sprintf(buf, "%08d", b.s[i]); buf[8] = '\0';
strncat(f[j], buf, 8);
}
}
f[j][42] = '\0';
Insert(f[j], j);
}

int t, kase = 0; cin >> t;
while(t--) {
cin >> tmp;
cout << "Case #" << ++kase <<": " << Find(tmp) <<endl;
}
Delete(root);
return 0;
}


以上。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: