PTA题解集合(更新中。。。)

去年参加了一次天梯赛,毕竟才大一,水平有限,L1基本答了,L2混了几题部分正确,L3题目还没点开过。今年再战,顺便更新题解,也作自己复习。另外,平常HDU题做多了,养成了动不动就用 scanfprintf的习惯,其实PTA的题基本上用 cincout都能过的,所以 下面的代码中的所有c语言部分为了方便都可以换成c++,毕竟scanf,printf不仅字母多,里面还要打一堆 %太繁琐了。

L1部分题解

由于L1题目普遍简单,所以备注比较少,后面的L2题目会适当加上备注。

L1-001

最难的一题!!!

#include 
using namespace std;
int main() {
    cout << "Hello World!";
    return 0;
}

L1-002

因为图形上下对称,所以可以先计算出上半图形的高度、宽度,这题主要就是考察逻辑思维。

#include 
using namespace std;

int main() {
    int n, height = 1, width = 1;
    char ch;
    cin >> n >> ch;
    --n;  // 先扣去最中间的一个字符
    while (true) {
        if (n - 2 * (width + 2) >= 0) {
            ++height;
            width += 2;
            n -= 2 * width;
        } else
            break;
    }
    for (int i = 0; i < height; ++i) {
        for (int j = 0; j < i; ++j) cout << ' ';
        for (int j = 0; j < 2 * height - 1 - 2 * i; ++j) cout << ch;
        cout << '\n';
    }
    for (int i = 0; i < height - 1; ++i) {
        for (int j = 0; j < height - 2 - i; ++j) cout << ' ';
        for (int j = 0; j < 2 * (i + 1) + 1; ++j) cout << ch;
        cout << '\n';
    }
    cout << n;
    return 0;
}

L1-003

虽然题目描述需要输入正整数,但是输入正整数需要一次一次取模才能得到各位,直接以字符串的方式输入就很简单。

#include 

using namespace std;

int main() {
    int cnt[10] = {0};
    string num;
    cin >> num;
    for (int i = 0; i < num.size(); ++i) ++cnt[num[i] - '0'];
    for (int i = 0; i < 10; ++i)
        if (cnt[i] > 0) cout << i << ':' << cnt[i] << '\n';
    return 0;
}

L1-004

没有什么难点,L1很多题都像这样。

#include 

using namespace std;

int main() {
    int F;
    cin >> F;
    int C = 5 * (F - 32) / 9;
    cout << "Celsius = " << C;
    return 0;
}

L1-005

把试机座位号当作下标,然后准考证号和考试座位号分别存入a, b数组,最后输出就好。(准考证号16位,int装不下)

#include 

using namespace std;

typedef long long LL;
LL x, a[1005];
int n, m, y, z, b[1005];

int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%lld%d%d", &x, &y, &z);
        a[y] = x;
        b[y] = z;
    }
    scanf("%d", &m);
    for (int i = 0; i < m; ++i) {
        scanf("%d", &y);
        printf("%lld %d\n", a[y], b[y]);
    }
    return 0;
}

L1-006

用循环模拟出可能的连续因子序列。需要注意几个优化的地方,for (int i = 2; i <= sqrt(n); ++i),很好理解,大于sqrt(n) 的数再随便乘一下就大于n了,肯定就不是因子,所以这里只走到sqrt(n)

#include 

using namespace std;

typedef long long LL;
LL n, tmp;
int start, len;

int main() {
    scanf("%lld", &n);
    for (int i = 2; i <= sqrt(n); ++i) {
        tmp = 1;
        for (int j = i; tmp * j <= n; ++j) {
            tmp *= j;
            if (n % tmp) break;  // 如果tmp不是n的因子,再乘任何数也不是
            if (j - i + 1 > len) {
                start = i;
                len = j - i + 1;
            }
        }
    }
    if (!start) {
    /** 最后一种质数的情况,质数只能被1和本身整除
     * 这是这题最坑的一个地方,百度了因子的定义
     * 因子并不包括数本身,因数才包括自己本身,例如10
     * 因子就是1,2,5  因数就是1,2,5,10
     * 可这题偏偏就要输出这个本身。。。
     */
        start = n;
        len = 1;
    }
    printf("%d\n%d", len, start);
    for (int i = 1; i < len; ++i) printf("*%d", start + i);
    return 0;
}

L1-007

只要注意行尾不能有空格就好了。

#include 

using namespace std;

string a[] = {"ling", "yi", "er", "san", "si", "wu", "liu", "qi", "ba", "jiu"};

int main() {
    string s;
    cin >> s;
    if (s[0] == '-')
        cout << "fu";
    else
        cout << a[s[0] - '0'];
    for (int i = 1; i < s.size(); ++i)
        cout << ' ' << a[s[i] - '0'];
    return 0;
}

L1-008

只能说pta的题太细节了,不难的一题但是正确率很低,因为很容易出现刚好5个数时,最后连换两行的情况。

#include 

using namespace std;

int main() {
    int a, b, sum = 0;
    scanf("%d%d", &a, &b);
    for (int i = a; i <= b; ++i) {
        sum += i;
        printf("%5d", i);
        if ((i - a + 1) % 5 == 0)
            printf("\n");
        else if (i == b)
            printf("\n");
    }
    printf("Sum = %d", sum);
    return 0;
}

L1-059

就是几个知识点,getline 前用 cin.get() 吸收回车,然后一些字符串基本操作,find 正向查找,rfind 反向查找,substr 切割。最后就是需要考虑到一种特殊情况,逗号前的字符不足3个,那么直接去使用 str.substr(p - 3, 3) == "ong" 判断的话就会出现错误(最后一组测试数据),所以需要加上 p >= 3 这个前提条件。

#include 

using namespace std;

int main() {
    string str;
    int n;
    cin >> n;
    cin.get();
    while (n--) {
        getline(cin, str);
        int p = str.find(',');
        if (p >= 3 && str.substr(p - 3, 3) == "ong" &&
            str.substr(str.size() - 4, 3) == "ong") {
            p = str.size();
            for (int i = 0; i < 3; ++i) p = str.rfind(' ', p - 1);
            cout << str.substr(0, p) << " qiao ben zhong.\n";
        } else
            cout << "Skipped\n";
    }
    return 0;
}

L2部分题解

L2-001 (dijkstra)

复杂了一些的最短路,用优先队列对查找最小dis的过程做了优化。dis[i]表示起点到i的最短路长度,pre[i]表示i的前驱,cnt[i]表示到达i的最短路条数,sum[i]表示最多的救援队数目。

#include 
#define INF 0x3f3f3f3f
#define N 505

using namespace std;

int n, m, s, d, x, y, z;
int num[N], path[N][N], dis[N], pre[N], cnt[N], sum[N];
bool vis[N];

struct Node {
    int key, value;
    Node(int k, int v) {
        key = k;
        value = v;
    }
    bool operator<(const Node &a) const { return value > a.value; }
};

void dijkstra() {
    pre[s] = -1;
    cnt[s] = 1;
    sum[s] = num[s];
    dis[s] = 0;
    priority_queue q;
    q.push(Node(s, 0));
    while (!q.empty()) {
        int k = q.top().key;
        q.pop();
        if (vis[k]) continue;
        vis[k] = true;
        for (int i = 0; i < n; ++i) {
            if (!vis[i]) {
                int cur_dis = dis[k] + path[k][i];
                int cur_sum = sum[k] + num[i];
                if (cur_dis < dis[i]) {
                    dis[i] = cur_dis;
                    pre[i] = k;
                    cnt[i] = cnt[k];
                    sum[i] = cur_sum;
                } else if (cur_dis == dis[i]) {
                    cnt[i] += cnt[k];
                    if (cur_sum > sum[i]) {
                        sum[i] = cur_sum;
                        pre[i] = k;
                    }
                }
                q.push(Node(i, dis[i]));
            }
        }
    }
    printf("%d %d\n", cnt[d], sum[d]);
    stack stk;
    int k = d;
    while (k != -1) {
        stk.push(k);
        k = pre[k];
    }
    printf("%d", stk.top());
    stk.pop();
    while (!stk.empty()) {
        printf(" %d", stk.top());
        stk.pop();
    }
    printf("\n");
}

int main() {
    memset(path, INF, sizeof(path));
    memset(dis, INF, sizeof(dis));
    scanf("%d%d%d%d", &n, &m, &s, &d);
    for (int i = 0; i < n; ++i) scanf("%d", &num[i]);
    for (int i = 0; i < m; ++i) {
        scanf("%d%d%d", &x, &y, &z);
        path[x][y] = path[y][x] = z;
    }
    dijkstra();
    return 0;
}

L2-004 (搜索树)

本以为上完数据结构的课应该就会各种树了,结果发现自己还是太年轻。这题按照二叉搜索树的性质建树,先试试是不是二叉搜索树,再试试是不是二叉搜索树的“镜像”,如果最后能建成就顺便输出后序遍历。

#include 

using namespace std;

int n, pre[1005], post[1005], cnt = 0;
bool flag;

void build(int l, int r) {
    if (l > r) return;
    int i = l + 1, j = r;
    if (!flag) {
        while (i <= r && pre[i] < pre[l]) ++i;
        while (l < j && pre[j] >= pre[l]) --j;
    } else {
        while (i <= r && pre[i] >= pre[l]) ++i;
        while (l < j && pre[j] < pre[l]) --j;
    }
    if (i - j != 1) return;
    build(l + 1, j);
    build(i, r);
    post[cnt++] = pre[l];
}

int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) scanf("%d", &pre[i]);
    build(0, n - 1);
    if (cnt != n) {
        flag = true;
        cnt = 0;
        build(0, n - 1);
    }
    if (cnt != n)
        printf("NO\n");
    else {
        printf("YES\n");
        printf("%d", post[0]);
        for (int i = 1; i < cnt; ++i) printf(" %d", post[i]);
        printf("\n");
    }
    return 0;
}

L2-008 (Manacher)

马拉车算法模板题,c(center)表示中间点,r(right)表示回文串的右边界。具体算法思路不说了,还有需要理解的就是r关于c的对称点为2 * c - i

#include 

using namespace std;

int manacher(string s) {
    string str = "$#";
    for (int i = 0; i < s.size(); ++i) {
        str.push_back(s[i]);
        str.push_back('#');
    }
    int c = 0, r = 0;
    vector p(str.size());
    for (int i = 1; i < p.size(); ++i) {
        p[i] = r > i ? min(p[2 * c - i], r - i + 1) : 1;
        while (str[i - p[i]] == str[i + p[i]]) ++p[i];
        if (r - c + 1 < p[i]) {
            c = i;
            r = i + p[i] - 1;
        }
    }
    // return s.substr((2 * c - r) / 2, r - c);
    return r - c;
}

int main() {
    string s;
    getline(cin, s);
    cout << manacher(s);
    return 0;
}

L2-009 (结构体排序)

结构体排序,输入的钱是分为单位,最后输出时除以100.0得到元。用结构体储存每个人的编号,收入,抢到红包数。

#include 
#define N 10005

using namespace std;

struct Node {
    int key, sum, cnt;
    Node() : sum(0), cnt(0) {}
} a[N];

int n, k, x, y;

bool cmp(Node a, Node b) {
    if (a.sum == b.sum) {
        if (a.cnt == b.cnt)
            return a.key < b.key;
        return a.cnt > b.cnt;
    }
    return a.sum > b.sum;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        a[i].key = i;
        scanf("%d", &k);
        while (k--) {
            scanf("%d%d", &x, &y);
            a[i].sum -= y;
            a[x].sum += y;
            ++a[x].cnt;
        }
    }
    sort(a + 1, a + n + 1, cmp);
    for (int i = 1; i <= n; ++i)
        printf("%d %.2f\n", a[i].key, a[i].sum / 100.0);
    return 0;
}

L2-010 (并查集)

并查集,如果两个人是朋友关系就合并,否则,用flag数组标记两人是敌人。最后直接判断一下输出就好。

#include 
#define N 105

using namespace std;

int n, m, k, x, y, z;
int f[N], _rank[N] = {0}, flag[N][N] = {false};

int _find(int x) {
    return x == f[x] ? x : f[x] = _find(f[x]);  // 路径压缩
}

void _union(int x, int y) {
    int f1 = _find(x), f2 = _find(y);
    if (f1 != f2) {
        if (_rank[f1] < _rank[f2])  // 按秩合并
            f[f1] = f2;
        else {
            f[f2] = f1;
            if (_rank[f1] == _rank[f2])
                ++_rank[f1];
        }
    }
}

int main() {
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; ++i)
        f[i] = i;
    while (m--) {
        scanf("%d%d%d", &x, &y, &z);
        if (z == 1)
            _union(x, y);
        else
            flag[x][y] = flag[y][x] = true;
    }
    while (k--) {
        scanf("%d%d", &x, &y);
        if (_find(x) == _find(y)) {
            if (!flag[x][y])
                printf("No problem\n");
            else
                printf("OK but...\n");
        } else {
            if (!flag[x][y])
                printf("OK\n");
            else
                printf("No way\n");
        }
    }
    return 0;
}

L2-012 (堆)

肝了我一晚,怎么提交都是部分正确,我是真的无奈,直到看到有个题解说,“用线性调整heap的话是不对的,题目要求是一个一个插入一个空的MinHeap,所以只能插入然后上浮”。我。。。

     ┏┓   ┏┓
    ┏┛┻━━━┛┻┓
    ┃       ┃
    ┃   ━   ┃
    ┃ ┳┛ ┗┳ ┃
    ┃       ┃
    ┃   ┻   ┃
    ┃       ┃
    ┗━┓   ┏━┛
      ┃   ┃
      ┃   ┃
      ┃   ┗━━━┓
      ┃       ┣┓
      ┃       ┏┛
      ┗┓┓┏━┳┓┏┛
       ┃┫┫ ┃┫┫
       ┗┻┛ ┗┻┛

思路很粗暴,先建堆(思路就是上面那句话,采用上浮的方式,加一个数浮一次),然后stringstream把单词分割出来装进vector,剩下就是简单判断。

#include 
#define N 1005

using namespace std;

int n, m, a[N];
string s;
stringstream ss;
vector v;

void swim(int i) {
    int parent = (i - 1) / 2;
    if (parent >= 0 && a[parent] > a[i]) {
        swap(a[i], a[parent]);
        swim(parent);
    }
}

int toInt(string &s) {
    stringstream ss;
    ss << s;
    int x;
    ss >> x;
    return x;
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        swim(i);
    }
    cin.get();
    for (int i = 0; i < m; ++i) {
        getline(cin, s);
        ss << s;
        while (ss >> s) v.push_back(s);
        if (v[3] == "root") {
            if (toInt(v[0]) == a[0])
                cout << "T\n";
            else
                cout << "F\n";
        } else if (v[4] == "siblings") {
            int p1 = find(a, a + n, toInt(v[0])) - a;
            int p2 = find(a, a + n, toInt(v[2])) - a;
            if (((p1 - 1) >> 1) == ((p2 - 1) >> 1))
                cout << "T\n";
            else
                cout << "F\n";
        } else if (v[3] == "parent") {
            int p1 = find(a, a + n, toInt(v[0])) - a;
            int p2 = find(a, a + n, toInt(v.back())) - a;
            if ((p2 - 1) >> 1 == p1)
                cout << "T\n";
            else
                cout << "F\n";
        } else {
            int p1 = find(a, a + n, toInt(v[0])) - a;
            int p2 = find(a, a + n, toInt(v.back())) - a;
            if ((p1 - 1) >> 1 == p2)
                cout << "T\n";
            else
                cout << "F\n";
        }
        v.clear();
        ss.clear();
    }
    return 0;
}

你可能感兴趣的:(acm,c++)