【PAT甲级】A1001-A1050刷题记录

文章目录


感觉一篇文章记录太多甲级的题目也不是太好,每一篇50道题好了。以后把通过率也加上,方便参考。

A1001 A+B Format (20 分) 0.25

−10​6​​≤ a,b ≤ 106​​,输出a+b的标准形式,即每三位一个","。小于四位数直接输出;负数先输出负号,再转为正数;正数顺位存储为字符串,每隔三位添加一个",",然后reverse输出。

#include <bits/stdc++.h>
using namespace std;
int main() {
    int a, b, c;
    scanf("%d%d", &a, &b);
    c = a + b;
    if ((c >= -999 && c < 0) || (c >= 0 && c <= 999)) 
	   printf("%d\n", c); //小于四位数直接输出
    else {
        if (c < 0) {
            printf("-");
            c = -c;
        }
        string s = ""; int sum = 0;
        do {
        	int t = c % 10;
            s += (t + '0'); sum++;
            if (sum % 3 == 0 && c / 10 != 0) s += ',';
            c /= 10;
        } while (c);
        reverse(s.begin(), s.end()); 
        cout << s << endl;
    }
    return 0;
}

简洁的写法如下,利用printf的格式化输出特性

#include <bits/stdc++.h>
int main() {
    int a, b, c;
    scanf("%d%d", &a, &b);
    c = a + b;
    if (c < 0) {
        printf("-");
        c = -c;
    }
    if (c >= 1000000) printf("%d,%03d,%03d", c / 1000000, c % 1000000 / 1000, c % 1000);
    else if (c >= 1000) printf("%d,%03d", c / 1000, c % 1000);
    else printf("%d", c); 
    return 0;
}

★(一元多项式加法) A1002 A+B for Polynomials (25 分) 0.21

This time, you are supposed to find A+B where A and B are two polynomials.

  • Input Specification:
    Each input file contains one test case. Each case occupies 2 lines, and each line contains the information of a polynomial:
    K N​1​​ aN​1​​​​ N​2​​ a​N​2​​​​ … N​K​​ a​N​K​​​​
    where K is the number of nonzero terms in the polynomial, N​i​​ and a​N​i​​​​(i=1,2,⋯,K) are the exponents and coefficients, respectively. It is given that 1≤K≤10,0≤N​K​​<⋯<N​2​​<N​1​​≤1000.
  • Output Specification:
    For each test case you should output the sum of A and B in one line, with the same format as the input. Notice that there must be NO extra space at the end of each line. Please be accurate to 1 decimal place.
  • Sample Input:
    2 1 2.4 0 3.2
    2 2 1.5 1 0.5
    
  • Sample Output:
    3 2 1.5 1 2.9 0 3.2
    

这道题目是一元多项式的经典操作-加法。并没有多难,不过一元多项式的存储结构非常灵活,有好几种结构,加法乘法和求导操作都是可能考的题目。乘法如A1009 Product of Polynomials,求导的在乙级有一道题目。

而我这里使用了多项式结构体,含有系数数组和最高次项的大小,加法乘法操作写起来都很简单,不过它更适合稠密多项式,如果一个多项式中间太多项为零,就会浪费很多时间。另外一种使用数组的方法是非零项结构体数组(用vector),只存储非零项,虽然操作写起来复杂了一点。最后一种就是使用链表了。

本题注意点:

  • (系数)精确到小数点后一位;
  • 以同样的格式输出两个多项式A+B的和,即先输出非零项数量,再从高次项往低次项输出指数和系数。要小心的是,如果结果为零多项式,则特判输出"0",不然会有一个测试点错误。
#include <bits/stdc++.h>
using namespace std;

const int maxn = 1002;
struct polynomial {
	int highPower;
	double coefArray[maxn];
	polynomial() {
		memset(coefArray, 0.0, sizeof(coefArray));
		highPower = 0;
	}
} p1, p2, ans;
void Init(polynomial &p) {
	int n, e, maxPower = -1; double c;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d%lf", &e, &c);
		p.coefArray[e] = c;
		if (e > maxPower) maxPower = e;
	}
	p.highPower = maxPower; //得到最高次项
}
void AddPolynomial(polynomial &p1, polynomial &p2) {
	int cnt = 0; //系数非零项的个数 
	ans.highPower = max(p1.highPower, p2.highPower);
	for (int i = ans.highPower; i >= 0; i--) { //相同指数的系数项相加
		ans.coefArray[i] = p1.coefArray[i] + p2.coefArray[i];
		if (ans.coefArray[i] != 0.0) cnt++;
	} 
	if (cnt == 0) printf("0\n"); //特判零多项式
	else {
		printf("%d", cnt);
		for (int i = ans.highPower; i >= 0; i--) {
			if (ans.coefArray[i] != 0.0) printf(" %d %.1lf", i, ans.coefArray[i]);
		} 
	} 
}
int main() {
	int c, e, maxPower = -1;
	Init(p1); Init(p2);
	AddPolynomial(p1, p2);
	return 0;
}

(单源最短路Dijkstra+边权+第二标尺(点权)+最短路数目) A1003 Emergency (25 分) 0.28

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

  • Input Specification:
    Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (≤500) - the number of cities (and the cities are numbered from 0 to N−1), M - the number of roads, C​1​​ and C​2​​ - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c​1​​, c​2​​ and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C​1​​ to C​2​​.
  • Output Specification:
    For each test case, print in one line two numbers: the number of different shortest paths between C​1​​ and C​2​​, and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.
  • Sample Input:
    5 6 0 2
    1 2 1 5 3
    0 1 1
    0 2 2
    0 3 1
    1 2 1
    2 4 1
    3 4 1
    
  • Sample Output:
    2 4
    

题意:给出一份地图,途中标记了城市和道路,道路的长度为边权,每个城市的急救队数量为点权,给你起点和目标城市,要求你尽快的前往目的地,同时集结最多的急救队伍。最后给出最短路的数目和最多的队伍数量。
思路:用Dijstra模版求出最短路即可。

#include <bits/stdc++.h>
using namespace std;
const int maxv = 1010, inf = 1000000000;
struct node {
	int to, dis;
};
int vis[maxv] = {false}, weight[maxv]; 
//从s出发到其他点的最短距离; 最大点权; 最短路数目 
int d[maxv], w[maxv], num[maxv]; 
vector<node> G[maxv];
int n, m, s, di; //结点数目, 边数, 起点, 终点 
void Dijkstra(int s) {
	fill(vis, vis + maxv, false);
	fill(d, d + maxv, inf);    d[s] = 0;
	fill(w, w + maxv, 0); 	   w[s] = weight[s];
	fill(num, num + maxv, 0);  num[s] = 1; 
	for (int i = 0; i < n; i++) {
		int min = inf, u = -1;
		for (int j = 0; j < n; j++) {
			if (vis[j] == false && d[j] < min) {
				u = j;
				min = d[j];
			} 
		}
		if (u == -1) return; 
		vis[u] = true;
		for (int k = 0; k < G[u].size(); k++) {
			int v = G[u][k].to; 
			if (vis[v] == false) { //未曾访问过该点 
				if (d[u] + G[u][k].dis < d[v]) { //以u为中介点可以使v到s的距离减少
				    d[v] = d[u] + G[u][k].dis; //更新距离 
			        num[v] = num[u]; //继承之前的最短路数目 
			        w[v] = w[u] + weight[v]; //点权更新 
				} else if (d[u] + G[u][k].dis == d[v]) { //不用更新距离 
					num[v] += num[u]; //加上新的最短路数目 
					if (w[u] + weight[v] > w[v]) w[v] = w[u] + weight[v];
				} 
			}
		} 
	} 
}

int main() {
	scanf("%d%d%d%d", &n, &m, &s, &di); //起点, 目的地 
	for (int i = 0; i < n; i++) scanf("%d", &weight[i]); //点权 
	int c1, c2, dis; 
	for (int i = 0; i < m; i++) { //边权 
		scanf("%d%d%d", &c1, &c2, &dis);
		G[c1].push_back(node{c2, dis});
		G[c2].push_back(node{c1, dis});
	}
	Dijkstra(s);
	printf("%d %d\n", num[di], w[di]);
	return 0;
}

(静态树+层次遍历) A1004 Counting Leaves (30 分) 0.35

A family hierarchy is usually presented by a pedigree tree. Your job is to count those family members who have no child.

  • Input Specification:
    Each input file contains one test case. Each case starts with a line containing 0<N<100, the number of nodes in a tree, and M (<N), the number of non-leaf nodes. Then M lines follow, each in the format:

    ID K ID[1] ID[2] ... ID[K]
    

    where ID is a two-digit number representing a given non-leaf node, K is the number of its children, followed by a sequence of two-digit ID’s of its children. For the sake of simplicity, let us fix the root ID to be 01.
    The input ends with N being 0. That case must NOT be processed.

  • Output Specification:
    For each test case, you are supposed to count those family members who have no child for every seniority level starting from the root. The numbers must be printed in a line, separated by a space, and there must be no extra space at the end of each line.

    The sample case represents a tree with only 2 nodes, where 01 is the root and 02 is its only child. Hence on the root 01 level, there is 0 leaf node; and on the next level, there is 1 leaf node. Then we should output 0 1 in a line.

  • Sample Input:

    2 1
    01 1 02
    
  • Sample Output:

    0 1
    

题意:给定一棵一般性质的树,问每层有多少叶子结点。这里我还用了层号,ans数组存储每层的叶子结点数目,max_level存储树的层数。seniority level的意思是从第一层(根结点)开始逐层输出叶子结点的个数

#include <bits/stdc++.h>
using namespace std;
const int maxn = 101;
struct Node {
    int layer;
    vector<int> child;
} node[maxn];
int ans[maxn]; //各层的叶子结点数目, 层号从0开始

int LevelOrder(int r) {
	int max_level = 1; //层数从1开始
    memset(ans, 0, sizeof(ans));
    node[r].layer = 0;
    queue<int> q;
    q.push(r);
    while (!q.empty()) {
        int front = q.front(); q.pop();
        if (node[front].layer + 1 > max_level) max_level++;
        if (node[front].child.size() == 0) ans[node[front].layer]++;
        for (int i = 0; i < node[front].child.size(); i++) {
            int child = node[front].child[i];
            node[child].layer = node[front].layer + 1;
            q.push(child);
        }
    }
    return max_level;
}

int main() {
    int n, nonleaf, id, k, t; //所有结点个数和非叶结点个数
    scanf("%d%d", &n, &nonleaf);
    for (int i = 0; i < nonleaf; i++) {
        scanf("%d%d", &id, &k);
        for (int j = 0; j < k; j++) {
            scanf("%d", &t);
            node[id].child.push_back(t);
        }
    }
    int m = LevelOrder(1);
    for (int i = 0; i < m; i++) {
        if (i > 0) printf(" ");
        printf("%d", ans[i]);
    }
    return 0;
}

A1005 Spell It Right (20 分) 0.34

Given a non-negative integer N, your task is to compute the sum of all the digits of N, and output every digit of the sum in English.

  • Input Specification:
    Each input file contains one test case. Each case occupies one line which contains an N (≤10​100​​).
  • Output Specification:
    For each test case, output in one line the digits of the sum in English words. There must be one space between two consecutive words, but no extra space at the end of a line.
  • Sample Input:
    12345
    
  • Sample Output:
    one five
    

这里运用了字符串版输出函数sprintf,将和以整型的形式打印进字符数组s,不用一位一位的倒着存入数组中,更方便。

#include <bits/stdc++.h>
char number[][10] = {"zero", "one", "two", "three", "four", "five", "six", "seven", 
"eight", "nine"}; 

int main() {
    char r[110], s[110]; 
    scanf("%s", r);
    int sum = 0;
    for (int i = 0; r[i]; i++) 
        sum += (r[i] - '0');
    sprintf(s, "%d", sum);
    printf("%s", number[s[0] - '0']);
    for (int i = 1; s[i]; i++) printf(" %s", number[s[i] - '0']);
    return 0;
}

A1006 Sign In and Sign Out (25 分)

times are given in the format HH:MM:SS。找出最早签到和最晚签离的人。这里的时间完全可以读入string,然后用string自带的比较预算符(基于字典序)比较,写起来更加简单。

#include <bits/stdc++.h>
using namespace std;
struct person {
    string name, inTime, outTime;
};

int main() {
    int m, e = 0, l = 0;
    scanf("%d", &m);
    person p[m];
    for (int i = 0; i < m; i++) {
        cin >> p[i].name >> p[i].inTime >> p[i].outTime;
        if (p[i].inTime < p[e].inTime) e = i;
        if (p[i].outTime > p[l].outTime) l = i;
    }
    cout << p[e].name << " " << p[l].name << endl;
    return 0;
}

麻烦一点的代码,这里设置了多个构造函数,为了初始化结构体的时候方便一点

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

struct node {
    char id[20];
    int hh, mm, ss; 
    node() {} //默认构造函数 
    node(int h, int m, int s) {
        hh = h; mm = m; ss = s;
    }
};

bool great(node n1, node n2) { //n1>n2返回true
    if (n1.hh != n2.hh) return n1.hh > n2.hh;
    if (n1.mm != n2.mm) return n1.mm > n2.mm;
    if (n1.ss != n2.ss) return n1.ss > n2.ss;
}

int main() {
    int m;
    scanf("%d", &m); //ans1存放最早签到时间, ans2存放最晚签离时间
    node temp, ans1(24, 60, 60), ans2(0, 0, 0); //最早签到设为最大, 最晚签离设为最小
    for (int i = 0; i < m; i++) {
        scanf("%s %d:%d:%d", temp.id, &temp.hh, &temp.mm, &temp.ss);
        if (great(temp, ans1) == false) ans1 = temp; //更小的签到时间
        scanf("%d:%d:%d", &temp.hh, &temp.mm, &temp.ss);
        if (great(temp, ans2) == true) ans2 = temp; //更晚的签离时间
    }
    printf("%s %s\n", ans1.id, ans2.id);
    return 0;
}

☆☆ A1007 Maximum Subsequence Sum (25 分) 0.19

题目大意:求最大连续子序列和,输出最大的和以及这个子序列的开始值和结束值。如果所有数都小于0,那么认为最大的和为0,并且输出首尾元素。我在https://blog.csdn.net/myRealization/article/details/95046115给出了分析。

#include <cstdio>
int a[10010];
int main()
{
    int K;
    scanf("%d", &K);
    int ThisSum, ThisLeft = 0;  // 指示当前子列和的左位序, 如果当前子列和不小于0不会变化  
    int MaxSum = -1, MaxLeft = 0, MaxRight = K-1; // 指示最大子列和的左右位序,需要更新的时候就分别更新
    for (int i = 0; i < K; i++) {
        scanf("%d", &a[i]);    
        ThisSum += a[i];
        if (ThisSum > MaxSum) {
            MaxSum = ThisSum;
            MaxLeft = ThisLeft; 
            MaxRight = i;     
        } else if (ThisSum < 0) { // 这里一定要用else if??
            ThisSum = 0;
            ThisLeft = i + 1;
        }
    }
    if (MaxSum < 0) MaxSum = 0;
    printf("%d %d %d", MaxSum, a[MaxLeft], a[MaxRight]);        
    return 0;
}

A1008 Elevator (20 分) 0.58

简单的电梯模拟。有一部电梯,最开始在0层,上一层楼6秒,下一层楼4秒,到达当前目的楼层后还需停留5秒。给出电梯需要去的楼层顺序,求总共需要花费多少时间(电梯最后不需要回到0层)。

#include <bits/stdc++.h>

int main() {
    int n; //All the numbers in the input are less than 100
    scanf("%d", &n); //n > 0
    int elev[n];
    for (int i = 0; i < n; i++) scanf("%d", &elev[i]);
    int time = 0;
    for (int i = 0; i < n; i++) { 
    	//一开始就要在第0层等5s
        if (i == 0 && elev[i] == 0) time += 5; 
        //一开始直接上去, 然后停下5s
        else if (i == 0 && elev[i] != 0) time += (elev[i] - 0) * 6 + 5; 
        else { //i > 0
            int t = elev[i] - elev[i - 1]; //层数
            time += t > 0 ? t * 6 + 5 : -t * 4 + 5; //上楼或下楼
        }
    }
    printf("%d\n", time);
    return 0;
}

这里还写麻烦了点,还可以更加简单。

★(一元多项式乘法) A1009 Product of Polynomials (25 分)

This time, you are supposed to find A×B where A and B are two polynomials.

  • Input Specification:
    Each input file contains one test case. Each case occupies 2 lines, and each line contains the information of a polynomial: K N​1​​ a​N​1​​​​ N​2​​ a​N​2​​​​ … N​K​​ a​N​K​​​​
    where K is the number of nonzero terms in the polynomial, N​i​​ and a​N​i​​​​ (i=1,2,⋯,K) are the exponents and coefficients, respectively. It is given that 1≤K≤10, 0≤N​K​​<⋯<N​2​​<N​1​​≤1000.
  • Output Specification:
    For each test case you should output the product of A and B in one line, with the same format as the input. Notice that there must be NO extra space at the end of each line. Please be accurate up to 1 decimal place.

这个题是A1002 A+B for Polynomials (25 分)的同类题,都是对一元多项式的操作,因此可以套用1002的多项式结构体模板。这里的指数最大只有1000,相乘最大是2000,因此系数数组要开到2000以上。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 2002;
struct polynomial {
	int highPower;
	double coefArray[maxn];
	polynomial() {
		memset(coefArray, 0.0, sizeof(coefArray));
		highPower = 0;
	}
} p1, p2, ans;
void Init(polynomial &p) {
	int n, e, maxPower = -1; double c;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d%lf", &e, &c);
		p.coefArray[e] = c;
		if (e > maxPower) maxPower = e;
	}
	p.highPower = maxPower; //得到最高次项
}
void MultiPolynomial(polynomial &p1, polynomial &p2) {
	int cnt = 0; //系数非零项的个数 
	ans.highPower = p1.highPower + p2.highPower;
	for (int i = 0; i <= p1.highPower; i++) { //全部乘一次
		for (int j = 0; j <= p2.highPower; j++) 
			ans.coefArray[i + j] += p1.coefArray[i] * p2.coefArray[j];   
	} 
    for (int i = 0; i <= ans.highPower; i++) {
        if (ans.coefArray[i] != 0.0) cnt++;
    }
	if (cnt == 0) printf("0\n"); //特判零多项式
	else {
		printf("%d", cnt);
		for (int i = ans.highPower; i >= 0; i--) {
			if (ans.coefArray[i] != 0.0) printf(" %d %.1lf", i, ans.coefArray[i]);
		} 
	} 
}
int main() {
	int c, e, maxPower = -1;
	Init(p1); Init(p2);
	MultiPolynomial(p1, p2);
	return 0;
}

★★(进制转换+二分) A1010 Radix (25 分) 0.11

Given a pair of positive integers, for example, 6 and 110, can this equation 6 = 110 be true? The answer is yes, if 6 is a decimal number and 110 is a binary number.

Now for any pair of positive integers N​1​​ and N​2​​, your task is to find the radix of one number while that of the other is given.

  • Input Specification:
    Each input file contains one test case. Each case occupies a line which contains 4 positive integers:
    N1 N2 tag radix
    
    Here N1 and N2 each has no more than 10 digits. A digit is less than its radix and is chosen from the set {0-9, a-z} where 0-9 represent the decimal numbers 0-9, and a-z represent the decimal numbers 10-35. The last number radix is the radix of N1 if tag is 1, or of N2 if tag is 2.
  • Output Specification:
    For each test case, print in one line the radix of the other number so that the equation N1 = N2 is true. If the equation is impossible, print Impossible. If the solution is not unique, output the smallest possible radix.
  • Sample Input 1:
    6 110 1 10
    
  • Sample Output 1:
    2
    
  • Sample Input 2:
    1 ab 1 2
    
  • Sample Output 2:
    Impossible
    

注意点:

  • 暴力枚举进制会超时。
  • 答案要么存在且唯一,要么不存在。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
ll Map[256];
ll inf = ((ll)1 << 63) - 1; //long long的最大值, 注意类型转换和加括号
void init() {
    for (char c = '0'; c <= '9'; c++) {
        Map[c] = c - '0'; //将'0'-'9'映射到0-9
    }
    for (char c = 'a'; c <= 'z'; c++) {
        Map[c] = c - 'a' + 10; //将'a'-'z'映射到10-35
    }
}
ll convertNum10(char a[], ll radix, ll t) { //将radix进制的a转换为十进制, t为进制上界
    ll ans = 0;
    for (int i = 0; a[i]; i++) {
        ans = ans * radix + Map[a[i]]; //进制转换
        if (ans < 0 || ans > t) return -1; //未知进制的数溢出或超过N1的十进制
    }
    return ans;
}
int findMaxDigit(char n2[]) { //求最大的数位
    int ans = -1;
    for (int i = 0; n2[i]; i++) {
        if (Map[n2[i]] > ans) ans = Map[n2[i]];
    }
    return ans + 1; //最大的数位为ans, 说明进制数的底线是ans+1
}

int cmp(char n2[], ll radix, ll t) { //N2字符串的十进制与t比较
    ll num = convertNum10(n2, radix, t); //将N2转换为十进制
    if (num < 0) return 1; //溢出, 肯定N2 > t
    if (t > num) return -1; //t稍大, 返回-1
    else if (t == num) return 0; //相等, 返回0
    else return 1; //num稍大, 返回1
}
//二分查找N2合适的进制, 使得N1和N2转换为十进制后相等
ll binarySearch(char n2[], ll left, ll right, ll t) { //二分求解N2的进制
    ll mid;
    while (left <= right) {
        mid = (left + right) / 2;
        int flag = cmp(n2, mid, t); //判断N2转换为十进制后与t进制比较
        if (flag == 0) return mid;  //找到解, 返回mid
        else if (flag == -1) left = mid + 1; //往右子区间继续查找
        else right = mid - 1; //往左子区间继续查找
    }
    return -1; //不存在解
}

char n1[20], n2[20], temp[20];
int main() {
    init();
    int tag, radix;
    scanf("%s %s %d%d", n1, n2, &tag, &radix);
    if (tag == 2) {
        strcpy(temp, n1);
        strcpy(n1, n2);
        strcpy(n2, temp); //交换, 以确定radix为n1的进制
    }
    ll t = convertNum10(n1, radix, inf); //将N1从radix进制转换为十进制
    ll low = findMaxDigit(n2); //找到n2中数位最大的位+1, 作为二分下界
    ll high = max(low, t) + 1; //二分进制的上界
    ll ans = binarySearch(n2, low, high, t); //二分
    if (ans == -1) printf("Impossible\n");
    else printf("%lld\n", ans); //N1和N2相等时
    return 0;
}

A1011 World Cup Betting (20 分)

很简单的题目。

#include <bits/stdc++.h>
char odds[3] = {'W', 'T', 'L'};

int main() {
	double ans = 1.0, a[3][3]; //WTL
	int max[3] = {0};
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 3; j++) {
			scanf("%lf", &a[i][j]);
			if (a[i][j] > a[i][max[i]]) max[i] = j;	
		} 
		printf("%c ", odds[max[i]]); //输出对应的比赛结果
		ans *= a[i][max[i]];
	}
	printf("%.2lf\n", (ans * 0.65 - 1) * 2); //输出最大收益
	return 0; 
} 

★(排序应用) A1012 The Best Rank (25 分) 0.25

To evaluate the performance of our first year CS majored students, we consider their grades of three courses only: C - C Programming Language, M - Mathematics (Calculus or Linear Algrbra), and E - English. At the mean time, we encourage students by emphasizing on their best ranks – that is, among the four ranks with respect to the three courses and the average grade, we print the best rank for each student.

For example, The grades of C, M, E and A - Average of 4 students are given as the following:

StudentID  C  M  E  A
310101     98 85 88 90
310102     70 95 88 84
310103     82 87 94 88
310104     91 91 91 91

Then the best ranks for all the students are No.1 since the 1st one has done the best in C Programming Language, while the 2nd one in Mathematics, the 3rd one in English, and the last one in average.

  • Input Specification:
    Each input file contains one test case. Each case starts with a line containing 2 numbers N and M (≤2000), which are the total number of students, and the number of students who would check their ranks, respectively. Then N lines follow, each contains a student ID which is a string of 6 digits, followed by the three integer grades (in the range of [0, 100]) of that student in the order of C, M and E. Then there are M lines, each containing a student ID.
  • Output Specification:
    For each of the M students, print in one line the best rank for him/her, and the symbol of the corresponding rank, separated by a space.
    The priorities of the ranking methods are ordered as A > C > M > E. Hence if there are two or more ways for a student to obtain the same best rank, output the one with the highest priority.
    If a student is not on the grading list, simply output N/A.
  • Sample Input:
    5 6
    310101 98 85 88
    310102 70 95 88
    310103 82 87 94
    310104 91 91 91
    310105 85 90 90
    310101
    310102
    310103
    310104
    310105
    999999
    
  • Sample Output:
    1 C
    1 M
    1 E
    1 A
    3 A
    N/A
    

这道题的关键在于读懂题目。每个考生都会有4个成绩,4个排名,要输出最好的那个排名,并且输出对应的科目。如果排名相同,则按题目中给出的优先级顺序输出。

有时候,不是用stl就更加方便的,这道题如果一开始用map存储id和学生结构体的映射,反而不方便后面的排序。而且这里的排序不是常见的多级排序,而是有多个平行排序。因此,我们必须更加仔细的设计结构。

集合、查询、排序、最值等是PAT考试常考的25分题,必须掌握。

#include <bits/stdc++.h>
using namespace std;
char dict[5] = {'A', 'C', 'M', 'E'}; //按优先级顺序方便输出
int ranklist[1000000][4] = {0}; //ranklist[id][0]-ranklist[id][3]分别对应几门科目的排行 
struct node {
	int score[4]; 
	int id;
} stu[2010];

int now; //指示用哪个科目进行排序
bool cmp(node &a, node &b) { //stu数组按照now科目分数递减排序 
	return a.score[now] > b.score[now];
} 

int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	int stuID, C, M, E, A; //为避免取整舍入, 这里的平均分直接存为总分 
	for (int i = 0; i < n; i++) {
		scanf("%d%d%d%d", &stu[i].id, &stu[i].score[1], &stu[i].score[2], &stu[i].score[3]);
		stu[i].score[0] = stu[i].score[1] + stu[i].score[2] + stu[i].score[3];
	}
	for (now = 0; now < 4; now++) {
		sort(stu, stu + n, cmp);
		ranklist[stu[0].id][now] = 1; //第一名
		for (int i = 1; i < n; i++) {
			if (stu[i].score[now] < stu[i - 1].score[now]) {
				ranklist[stu[i].id][now] = i + 1; //为其设置正确的排名 
			} else if (stu[i].score[now] == stu[i - 1].score[now]) {
				ranklist[stu[i].id][now] = ranklist[stu[i - 1].id][now]; //排名相同  
			}
		} 
	} 
	int query; //查询的考生id
	while (m--) {
		scanf("%d", &query);
		if (ranklist[query][0] == 0) printf("N/A\n"); //考生ID不存在
		else {
			int k = 0; 
			for (int i = 1; i < 4; i++) {
				if (ranklist[query][i] < ranklist[query][k]) {
					k = i;
				}
			}
			printf("%d %c\n", ranklist[query][k], dict[k]);
		}
	} 
	return 0;
} 

A1013 Battle Over Cities 25 7093 22671 0.31

1013 Battle Over Cities (25 分)

It is vitally important to have all the cities connected by highways in a war. If a city is occupied by the enemy, all the highways from/toward that city are closed. We must know immediately if we need to repair any other highways to keep the rest of the cities connected. Given the map of cities which have all the remaining highways marked, you are supposed to tell the number of highways need to be repaired, quickly.

For example, if we have 3 cities and 2 highways connecting city​1​​-city​2​​ and city​1​​-city​3​​. Then if city​1​​ is occupied by the enemy, we must have 1 highway repaired, that is the highway city​2​​-city​3​​.
Input Specification:

Each input file contains one test case. Each case starts with a line containing 3 numbers N (<1000), M and K, which are the total number of cities, the number of remaining highways, and the number of cities to be checked, respectively. Then M lines follow, each describes a highway by 2 integers, which are the numbers of the cities the highway connects. The cities are numbered from 1 to N. Finally there is a line containing K numbers, which represent the cities we concern.
Output Specification:

For each of the K cities, output in a line the number of highways need to be repaired if that city is lost.
Sample Input:

3 2 3
1 2
1 3
1 2 3

Sample Output:

1
0
0

#include <bits/stdc++.h>
using namespace std;
const int maxv = 1010;
int vis[maxv], n, lost;
vector<int> G[maxv];
void dfs(int v) {
	vis[v] = true;
	for (int i = 0; i < G[v].size(); i++) {
		int u = G[v][i];
		if (vis[u] == false) dfs(u);
	}
}
int trave() {
	memset(vis, 0, sizeof(vis));
	vis[lost] = 1; //标记这个结点已经被敌人占据, 
	//因此不会访问这个结点及边 
	int comps = 0;
	for (int i = 1; i <= n; i++) {
		if (vis[i] == false) {
			comps++;
			dfs(i);
		}
	}
	return comps;
}
int main() {
	int m, k, u, v;
	scanf("%d%d%d", &n, &m, &k); 
	for (int i = 0; i < m; i++) {
		scanf("%d%d", &u, &v); //从1编号  
		G[u].push_back(v);
		G[v].push_back(u); //无向图 
	}
	while (k--) {
		scanf("%d", &lost); //该城市被攻陷了
		printf("%d\n", trave() - 1);  
	}
	return 0;
}

A1014 Waiting in Line 30 3393 15922 0.21

(素数) A1015 Reversible Primes (20 分) 0.26

A reversible prime in any number system is a prime whose “reverse” in that number system is also a prime. For example in the decimal system 73 is a reversible prime because its reverse 37 is also a prime.

Now given any two positive integers N (<10​5​​) and D (1<D≤10), you are supposed to tell if N is a reversible prime with radix D

题意:一个数制系统中的可逆素数是指,它是一个素数,且它的反转在这个数制系统中仍然是素数。因此,题目中首先给定的是一个10进制的数,如果它不是素数就直接输出No;如果它是素数,那么表示为D数制,并反转,得到的数在十进制下仍然是素数就输出Yes,不是的话就输出No。

#include <bits/stdc++.h>
const int maxn = 100100;
int p[maxn] = {1, 1, 0};
void find_prime() {
    for (int i = 2; i < maxn; i++) 
        if (p[i] == 0) 
            for (int j = i + i; j < maxn; j += i)
                p[j] = 1;
}
int main() {
    int n, radix;
	find_prime(); 
    while (~scanf("%d%d", &n, &radix)) {
        if (n < 0) break; //负数时退出
        if (p[n] != 0) printf("No\n"); //n不是素数, 输出No, 结束算法
		else {
            int d[50], len = 0, sum = 0; //进制转换
            do {
                d[len++] = n % radix;
                n /= radix;
            } while (n);
            for (int i = 0; i < len; i++)  //逆序转换为十进制
                sum = sum * radix + d[i];
            if (p[sum] == 0) printf("Yes\n"); 
            else printf("No\n");
        }
    }
    return 0;
}

★★★(复杂模拟) A1016 Phone Bills (25 分) 0.21

A long-distance telephone company charges its customers by the following rules:
Making a long-distance call costs a certain amount per minute, depending on the time of day when the call is made. When a customer starts connecting a long-distance call, the time will be recorded, and so will be the time when the customer hangs up the phone. Every calendar month, a bill is sent to the customer for each minute called (at a rate determined by the time of day). Your job is to prepare the bills for each month, given a set of phone call records.

  • Input Specification:
    Each input file contains one test case. Each case has two parts: the rate structure, and the phone call records.
    The rate structure consists of a line with 24 non-negative integers denoting the toll (cents/minute) from 00:00 - 01:00, the toll from 01:00 - 02:00, and so on for each hour in the day.
    The next line contains a positive number N (≤1000), followed by N lines of records. Each phone call record consists of the name of the customer (string of up to 20 characters without space), the time and date (mm:dd:hh:mm), and the word on-line or off-line.
    For each test case, all dates will be within a single month. Each on-line record is paired with the chronologically next record for the same customer provided it is an off-line record. Any on-line records that are not paired with an off-line record are ignored, as are off-line records not paired with an on-line record. It is guaranteed that at least one call is well paired in the input. You may assume that no two records for the same customer have the same time. Times are recorded using a 24-hour clock.
  • Output Specification:
    For each test case, you must print a phone bill for each customer.
    Bills must be printed in alphabetical order of customers’ names. For each customer, first print in a line the name of the customer and the month of the bill in the format shown by the sample. Then for each time period of a call, print in one line the beginning and ending time and date (dd:hh:mm), the lasting time (in minute) and the charge of the call. The calls must be listed in chronological order. Finally, print the total charge for the month in the format shown by the sample.
  • Sample Input:
    10 10 10 10 10 10 20 20 20 15 15 15 15 15 15 15 20 30 20 15 15 10 10 10
    10
    CYLL 01:01:06:01 on-line
    CYLL 01:28:16:05 off-line
    CYJJ 01:01:07:00 off-line
    CYLL 01:01:08:03 off-line
    CYJJ 01:01:05:59 on-line
    aaa 01:01:01:03 on-line
    aaa 01:02:00:01 on-line
    CYLL 01:28:15:41 on-line
    aaa 01:05:02:24 on-line
    aaa 01:04:23:59 off-line
    
  • Sample Output:
    CYJJ 01
    01:05:59 01:07:00 61 $12.10
    Total amount: $12.10
    CYLL 01
    01:06:01 01:08:03 122 $24.40
    28:15:41 28:16:05 24 $3.85
    Total amount: $28.25
    aaa 01
    02:00:01 04:23:59 4318 $638.80
    Total amount: $638.80
    

这道模拟题以后再做一下。

#include <bits/stdc++.h>
using namespace std;
struct bill {
	char name[25];
	int month, dd, hh, mm; //月天时分 
	int flag; 
} bills[1010], temp;
bool cmp(bill &a, bill &b) {
	int t = strcmp(a.name, b.name);
	if (t != 0) return t < 0; //按照字典序递增排列
    else if (a.month != b.month) return a.month < b.month; 
	else if (a.dd != b.dd) return a.dd < b.dd;
	else if (a.hh != b.hh) return a.hh < b.hh;
	else return a.mm < b.mm; 
}
int toll[30];
void getBills(int on, int off, int &time, int &money) {
	temp = bills[on]; 
	while (temp.dd < bills[off].dd || temp.hh < bills[off].hh || temp.mm < bills[off].mm) {
		time++; //该次记录总时间加1min
		money += toll[temp.hh]; //话费增加toll[temp.hh] 
		temp.mm++; 
		if (temp.mm >= 60) { //当前时间加1min 
			temp.mm = 0; //当前时间达到60min 
			temp.hh++; //进入下一个小时 
		}
		if (temp.hh >= 24) {
			temp.hh = 0;
			temp.dd++; //进入下一天 
		}
	}
} 
int main() {
	for (int i = 0; i < 24; i++) 
		scanf("%d", &toll[i]);
	int n; 
	scanf("%d", &n);
	char s[15];
	for (int i = 0; i < n; i++) { 
		scanf("%s %d:%d:%d:%d", bills[i].name, &bills[i].month, &bills[i].dd, &bills[i].hh, &bills[i].mm);
		scanf("%s", s);
		if (s[1] == 'n') bills[i].flag = 0;
		else bills[i].flag = 1;
	}
	sort(bills, bills + n, cmp);
	int on = 0, off, next; //on和off为配对的两条记录, next为下一个用户 
	while (on < n) { //每次循环处理个用户的所有记录 
		int needPrint = 0; //表示该用户是否需要输出 
		next = on; //从当前位置开始寻找下一个用户
		while (next < n && strcmp(bills[next].name, bills[on].name) == 0) {
			if (needPrint == 0 && bills[next].flag == 0) {
				needPrint = 1; //找到on, 置needPrint为1 
			} else if (needPrint == 1 && bills[next].flag == 1) {
				needPrint = 2;
			}
			next++; //next自增, 直到找到不同名字, 即下一个用户 
		} 
		if (needPrint < 2) { //没有找到配对的on-off 
			on = next;
			continue;
		} 
		int AllMoney = 0; //总共花费的钱
		printf("%s %02d\n", bills[on].name, bills[on].month); 
		while (on < next) { //寻找该用户的所有配对 
			while (on < next - 1 && !(bills[on].flag == 0 && bills[on + 1].flag == 1)) 
				on++; //直到找到连续的on-off 
			off = on + 1; //off必须是on的下一个
			if (off == next) { //已经输出完毕该用户所有配对的on-off 
				on = next;
				break; 
			} 
			printf("%02d:%02d:%02d ", bills[on].dd, bills[on].hh, bills[on].mm);
			printf("%02d:%02d:%02d ", bills[off].dd, bills[off].hh, bills[off].mm);
			int time = 0, money = 0; //时间、单次记录中花费的钱
		    getBills(on, off, time, money); //计算on到off内的时间和金钱
			AllMoney += money;
			printf("%d $%.2lf\n", time, money / 100.0);
			on = off + 1; //完成一个配对, 从off+1开始找下一对 
		}
		printf("Total amount: $%.2lf\n", AllMoney / 100.0); 
	}
	return 0;
}

A1017 Queueing at Bank 25 3024 12345 0.24

A1018 Public Bike Management 30 4137 16557 0.25

A1019 General Palindromic Number (20 分)

A number that will be the same when it is written forwards or backwards is known as a Palindromic Number. For example, 1234321 is a palindromic number. All single digit numbers are palindromic numbers.

Although palindromic numbers are most often considered in the decimal system, the concept of palindromicity can be applied to the natural numbers in any numeral system. Consider a number N>0 in base b≥2, where it is written in standard notation with k+1 digits a​i​​ as ∑​i=0​k​​(a​i​​b​i​​). Here, as usual, 0≤a​i​​<b for all i and a​k​​ is non-zero. Then N is palindromic if and only if a​i​​=a​k−i​​ for all i. Zero is written 0 in any base and is also palindromic by definition.

Given any positive decimal integer N and a base b, you are supposed to tell if N is a palindromic number in base b.

  • Input Specification:
    Each input file contains one test case. Each case consists of two positive numbers N and b, where 0<N≤10​9​​ is the decimal number and 2≤b≤10​9​​ is the base. The numbers are separated by a space.
  • Output Specification:
    For each test case, first print in one line Yes if N is a palindromic number in base b, or No if not. Then in the next line, print N as the number in base b in the form “a​k​​ a​k−1​​ … a​0​​”. Notice that there must be no extra space at the end of output.
  • Sample Input 1:
    27 2
    
  • Sample Output 1:
    Yes
    1 1 0 1 1
    
  • Sample Input 2:
    121 5
    
  • Sample Output 2:
    No
    4 4 1
    

一个小于整型范围的正十进制整数在转换成b进制(b小于整型范围)的数后,判断转换结果是否是回文数。很简单了。

#include <bits/stdc++.h>

int main() {
    int n, b, data[50], len = 0;
    scanf("%d%d", &n, &b);
    do {
        data[len++] = n % b;
        n /= b;
    } while (n);
    int flag = 1;
    for (int left = 0, right = len - 1; left < right; left++, right--) {
        if (data[left] != data[right]) { 
            printf("No\n");
            flag = 0; break;
        }
    }
    if (flag) printf("Yes\n"); //所有对称位置的数字都相同
    for (int i = len - 1; i >= 0; i--) { //输出转换结果数组
        if (i < len - 1) printf(" ");
        printf("%d", data[i]);
    }
    return 0;
}

(中序后序建静态二叉树->层序遍历) A1020 Tree Traversals (25 分) 0.47

Suppose that all the keys in a binary tree are distinct positive integers. Given the postorder and inorder traversal sequences, you are supposed to output the level order traversal sequence of the corresponding binary tree.

  • Input Specification:
    Each input file contains one test case. For each case, the first line gives a positive integer N (≤30), the total number of nodes in the binary tree. The second line gives the postorder sequence and the third line gives the inorder sequence. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, print in one line the level order traversal sequence of the corresponding binary tree. All the numbers in a line must be separated by exactly one space, and there must be no extra space at the end of the line.
  • Sample Input:
    7
    2 3 1 5 7 6 4
    1 2 3 4 5 6 7
    
  • Sample Output:
    4 1 6 3 5 7 2
    
#include <bits/stdc++.h>
using namespace std;
struct node {
	int data;
	node *left, *right;
};
vector<int> post(35), In(35); 
int n;
node* create(int r, int begin, int end) {
    if (begin > end) return NULL; //中序序列区间长度为0 
    node *root = new node; //建立一个根结点, 用来存放当前二叉树的根 
    root->data = post[r];
    int k = begin;
    while (k <= end && In[k] != post[r]) k++;
	int numright = end - k; //右子树的结点个数 
	root->left = create(r - numright - 1, begin, k - 1);
	root->right = create(r - 1, k + 1, end);
	return root;
}
int num = 0; //已经输出的结点个数 
void levelOrder(node *root) {
	queue<node *> q; //队列存储地址
	q.push(root); 
	while (!q.empty()) {
		node *now = q.front(); q.pop();
		printf("%d", now->data); //访问队首元素
	    num++;
	    if (num < n) printf(" ");
	    if (now->left != NULL) q.push(now->left);
	    if (now->right != NULL) q.push(now->right);
	} 
}

int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%d", &post[i]);
    for (int i = 0; i < n; i++) scanf("%d", &In[i]);
    node *root = create(n - 1, 0, n - 1);  
	levelOrder(root); //层序遍历 
    return 0;
}

A1021 Deepest Root 25 4511 17381 0.26

★★★(复杂模拟+查询) A1022 Digital Library 30 4239 13804 0.31

这里我学会了另一种方法如何让一个键对应多个值, 即声明map<type1, set<type2>>(用multimap说不定也行?可是它会将值有序排列吗?)。

题目要求我们根据某个除了编号的信息查询所有满足该信息的数的编号,并要求output the resulting book ID's in increasing order,这时我们就要想到用map或set这样的自动排序的数据结构。而且我们要根据某个信息查找所有编号,所以我们需要用map嵌套set。为了加快速度,使用了unordered_map。

#include <bits/stdc++.h>
using namespace std;
//分别建立书名作者关键词出版社和出版年份与书id的关系 
unordered_map<string, set<int> > mpTitle, mpAuthor, mpKey, mpPub, mpYear;
int query(unordered_map<string, set<int> > &mp, string &s) { //在mp中查找str 
	if (mp.find(s) == mp.end()) printf("Not Found\n"); //没找到 
	else for (auto it : mp[s]) printf("%07d\n", it); //输出s对应的所有id 
}
 
int main() {
	int n, m, id, type; 
	scanf("%d", &n); 
	string title, author, key, pub, year;
	for (int i = 0; i < n; i++) {
		scanf("%d\n", &id); //id;
		getline(cin, title); //接受书名
		mpTitle[title].insert(id); //将id插入到title对应的集合中
		getline(cin, author); //接受作者 
		mpAuthor[author].insert(id); //将id插入到author对应的集合中
		while (cin >> key) { //每次读入单个关键字 
			mpKey[key].insert(id); //将id插入到关键字对应的集合中 
			char c = getchar();
			if (c == '\n') break; //换行符表示关键词输入结束 
		} 
		getline(cin, pub); //接受出版商
		mpPub[pub].insert(id); //将id插入到出版商对应的集合中
		getline(cin, year); //接受出版时间
		mpYear[year].insert(id); //将id插入到出版时间对应的集合中
	}
	string temp;
	scanf("%d", &m);
	for (int i = 0; i < m; i++) {
		scanf("%d: ", &type); //查询类型	
		getline(cin, temp);
		cout << type << ": " << temp << endl; //输出类型和欲查询的字符串
		if (type == 1) query(mpTitle, temp);
		else if (type == 2) query(mpAuthor, temp);
		else if (type == 3) query(mpKey, temp);
		else if (type == 4) query(mpPub, temp);
		else query(mpYear, temp);	
	}
	return 0;
}

A1023 Have Fun with Numbers (20 分) 0.31

Notice that the number 123456789 is a 9-digit number consisting exactly the numbers from 1 to 9, with no duplication. Double it we will obtain 246913578, which happens to be another 9-digit number consisting exactly the numbers from 1 to 9, only in a different permutation. Check to see the result if we double it again!

Now you are suppose to check if there are more numbers with this property. That is, double a given number with k digits, you are to tell if the resulting number consists of only a permutation of the digits in the original number.

  • Input Specification:
    Each input contains one test case. Each case contains one positive integer with no more than 20 digits.
  • Output Specification:
    For each test case, first print in a line “Yes” if doubling the input number gives a number that consists of only a permutation of the digits in the original number, or “No” if not. Then in the next line, print the doubled number.
  • Sample Input:
    1234567899
    
  • Sample Output:
    Yes
    2469135798
    

注意点:

  • 无论是Yes还是No,结尾都要输出翻倍的数字。
#include <bits/stdc++.h>
int main() {
	char orig[25]; 
    int hashT1[20], hashT2[20], temp[25]; 
    memset(hashT1, 0, sizeof(hashT1)); //哈希1存储orig中数字分别出现的次数
    memset(hashT2, 0, sizeof(hashT2)); //哈希2存储temp中数字分别出现的次数
    scanf("%s", orig); 
    for (int i = 0; orig[i]; i++) hashT1[orig[i] - '0']++; 
    
	int len = strlen(orig), carry = 0, dL = 0;
    for (int i = len - 1; i >= 0; i--) { //原字符数组代表的数字*2
        int t = (orig[i] - '0') * 2 + carry;
        temp[dL++] = t % 10; //逆序存储到temp数组中
        hashT2[t % 10]++; 
        carry = t / 10;
    }
    if (carry != 0) temp[dL++] = carry; 

    int flag = 0;
    for (int i = 0; i < 10; i++) {
        if (hashT1[i] != hashT2[i]) {
            printf("No\n");
            flag = 1; 
            break;
        }
    }
    if (!flag) printf("Yes\n");  //无论如何, 结尾都要打印翻倍的数字
    for (int i = dL - 1; i >= 0; i--) printf("%d", temp[i]);
    printf("\n");
    return 0;
}

(大整数) A1024 Palindromic Number (25 分) 0.29

A number that will be the same when it is written forwards or backwards is known as a Palindromic Number. For example, 1234321 is a palindromic number. All single digit numbers are palindromic numbers.

Non-palindromic numbers can be paired with palindromic ones via a series of operations. First, the non-palindromic number is reversed and the result is added to the original number. If the result is not a palindromic number, this is repeated until it gives a palindromic number. For example, if we start from 67, we can obtain a palindromic number in 2 steps: 67 + 76 = 143, and 143 + 341 = 484.

Given any positive integer N, you are supposed to find its paired palindromic number and the number of steps taken to find it.

  • Input Specification:
    Each input file contains one test case. Each case consists of two positive numbers N and K, where N (≤10​10​​) is the initial numer and K (≤100) is the maximum number of steps. The numbers are separated by a space.
  • Output Specification:
    For each test case, output two numbers, one in each line. The first number is the paired palindromic number of N, and the second number is the number of steps taken to find the palindromic number. If the palindromic number is not found after K steps, just output the number obtained at the Kth step and K instead.
  • Sample Input 1:
    67 3
    
  • Sample Output 1:
    484
    2
    
  • Sample Input 2:
    69 3
    
  • Sample Output 2:
    1353
    3
    

N在操作100次后, 将远远大于long long的表示范围,因此必须使用大整数运算。另外,当题目给出的数字已经是回文数的时候,不需要对其进行操作,直接输出该数和0就可以了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct bign {
	int d[100], len;
	bign() {
		memset(d, 0, sizeof(d));
		len = 0;
	}
	bign(ll n) {
		memset(d, 0, sizeof(d));
		len = 0;
		do {
	        d[len++] = n % 10;
	        n /= 10;
	    } while (n);
	}
};
bign add(bign a, bign b) {
	bign c;
	int carry = 0;
	for (int i = 0; i < a.len || i < b.len; i++) {
		int t = a.d[i] + b.d[i] + carry;
		c.d[c.len++] = t % 10;
		carry = t / 10;
	}
	if (carry) c.d[c.len++] = carry;
	return c;
}
void print(bign a) {
	for (int i = a.len - 1; i >= 0; i--) 
		printf("%d", a.d[i]);
	printf("\n");
}
bool isPalin(bign a) { //判断一个数是否是回文数
    for (int le = 0, ri = a.len - 1; le < ri; le++, ri--) 
    	if (a.d[le] != a.d[ri]) return false;
    return true;
}
int main() {
    ll n, steps, k = 0;
    scanf("%lld%lld", &n, &steps);
    bign a(n);
    while (k < steps && isPalin(a) == false) { //不超过操作上限且a非回文
        bign rev = a;
        reverse(rev.d, rev.d + rev.len);
        a = add(a, rev);
        k++;
    }
    print(a);
    printf("%lld\n", k);
    return 0;
}

A1025 PAT Ranking (25 分) 0.26

Programming Ability Test (PAT) is organized by the College of Computer Science and Technology of Zhejiang University. Each test is supposed to run simultaneously in several places, and the ranklists will be merged immediately after the test. Now it is your job to write a program to correctly merge all the ranklists and generate the final rank.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains a positive number N (≤100), the number of test locations. Then N ranklists follow, each starts with a line containing a positive integer K (≤300), the number of testees, and then K lines containing the registration number (a 13-digit number) and the total score of each testee. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, first print in one line the total number of testees. Then print the final ranklist in the following format:
    registration_number final_rank location_number local_rank
    
    The locations are numbered from 1 to N. The output must be sorted in nondecreasing order of the final ranks. The testees with the same score must have the same rank, and the output must be sorted in nondecreasing order of their registration numbers.
  • Sample Input:
    2
    5
    1234567890001 95
    1234567890005 100
    1234567890003 95
    1234567890002 77
    1234567890004 85
    4
    1234567890013 65
    1234567890011 25
    1234567890014 100
    1234567890012 85
    
  • Sample Output:
    9
    1234567890005 1 1 1
    1234567890014 1 2 1
    1234567890001 3 1 2
    1234567890003 3 1 2
    1234567890004 5 1 4
    1234567890012 5 2 2
    1234567890002 7 1 5
    1234567890013 8 2 3
    1234567890011 9 2 4
    

题意:有n个考场,每个考场有若干个考生,要求分别进行考场内排序和全部考生排序。规则是①分数不同,按分数从大到小排序;②分数相同,按准考证号从小到大排序。

#include <bits/stdc++.h>
using namespace std;
struct testee {
	int lrank, frank, lonum;
    char renum[15];
    int total; 
} test[30100];
int num = 0, start = 0;
bool cmp(testee &a, testee &b) {
    if (a.total != b.total) return a.total > b.total;
	else return strcmp(a.renum, b.renum) < 0; //同样的分数, 注册号升序 
}

int main() {
	int n, k;
	scanf("%d", &n); //1-n个locations
    for (int i = 1; i <= n; i++) {
    	scanf("%d", &k);
    	for (int j = 0; j < k; j++) {
    		scanf("%s %d", test[num].renum, &test[num].total);
    		test[num].lonum = i;
    		num++;
		}
		sort(test + start, test + num, cmp);
		test[start].lrank = 1; //每个考点各自的排名 
		for (int i = start + 1; i < num; i++) {
			if (test[i].total < test[i - 1].total) {
				test[i].lrank = i - start + 1;
			} else {
				test[i].lrank = test[i - 1].lrank;
			}
		}
		start += k;
	} 
	sort(test, test + num, cmp);
	test[0].frank = 1; //将所有考生排序
	for (int i = 1; i < num; i++) {
		if (test[i].total < test[i - 1].total) {
			test[i].frank = i + 1;
		} else {
			test[i].frank = test[i - 1].frank;
		}
	}
	printf("%d\n", num);
	for (int i = 0; i < num; i++) 
		printf("%s %d %d %d\n", test[i].renum, test[i].frank, test[i].lonum, test[i].lrank);
	return 0;
}

A1026 Table Tennis 30 1506 9700 0.16

A1027 Colors in Mars (20 分) 0.50

正常的进制转换写法,不过这里还写复杂了一点。

#include <bits/stdc++.h>
char t[3] = {'A', 'B', 'C'};
int main() {
    int n = 3;
    printf("#"); //先打印#号
    while (n--) {
        int color, len = 0; 
        scanf("%d", &color);
        char data[4];
        do {
            int k = color % 13;
            data[len++] = k <= 9 ? k + '0' : t[k - 10];
            color /= 13;
        } while (color);
        if (len == 1) printf("0"); //只有一位数时, 多打印一个0
        for (int i = len - 1; i >= 0; i--) 
            printf("%c", data[i]);
    }
    return 0;
}

只要建立0-13到0-9A-C的映射关系,然后将每个10进制二位整数分别整除13(得a)、取余13(得b),然后输出a和b即可。方便的是,不用根据位数检查是否需要输出多余的0

#include <bits/stdc++.h>
char t[13] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C'};
int main() {
    int r, g, b; 
    scanf("%d%d%d", &r, &g, &b);
    printf("#"); //先打印#号
    printf("%c%c", t[r / 13], t[r % 13]);
    printf("%c%c", t[g / 13], t[g % 13]);
    printf("%c%c", t[b / 13], t[b % 13]);
    return 0;
}

A1028 List Sorting (25 分) 0.34

给定n个考生的准考证号,按照C=1,2,3的要求分别实现3个排序函数即可。

#include <bits/stdc++.h>
using namespace std;
struct person {
	int id;
	char name[15];
	int grade;
} excel[100100];
 
bool cmp1(person &a, person &b) { return a.id < b.id; } //ID升序 
bool cmp2(person &a, person &b) { 
    int t = strcmp(a.name, b.name);
    if (t != 0) return t < 0; //名字升序 
    else return a.id < b.id; //ID升序 
} 
bool cmp3(person &a, person &b) { 
    if (a.grade != b.grade) return a.grade < b.grade; //成绩升序 
    else return a.id < b.id;  //ID升序 
} 
int main()  {
	int n, c; scanf("%d%d", &n, &c);
	for (int i = 0; i < n; i++) 
		scanf("%d %s %d", &excel[i].id, excel[i].name, &excel[i].grade);
	switch (c) {
		case 1: sort(excel, excel + n, cmp1); break; 
		case 2: sort(excel, excel + n, cmp2); break; 
		case 3: sort(excel, excel + n, cmp3); break; 
	}
	for (int i = 0; i < n; i++) 
		printf("%06d %s %d\n", excel[i].id, excel[i].name, excel[i].grade);
	return 0;
}

A1029 Median (25 分) 0.10 内存限制: 1.5 MB

Given an increasing sequence S of N integers, the median is the number at the middle position. For example, the median of S1 = { 11, 12, 13, 14 } is 12, and the median of S2 = { 9, 10, 15, 16, 17 } is 15. The median of two sequences is defined to be the median of the nondecreasing sequence which contains all the elements of both sequences. For example, the median of S1 and S2 is 13.

Given two increasing sequences of integers, you are asked to find their median.

  • Input Specification:
    Each input file contains one test case. Each case occupies 2 lines, each gives the information of a sequence. For each sequence, the first positive integer N (≤2×10​5​​) is the size of that sequence. Then N integers follow, separated by a space. It is guaranteed that all the integers are in the range of long int.
  • Output Specification:
    For each test case you should output the median of the two given sequences in a line.
  • Sample Input:
    4 11 12 13 14
    5 9 10 15 16 17
    
  • Sample Output:
    13
    

将两个递增序列合并后输出中位数,偶数个数的序列输出左半部分最后一个数字。如果不是内存限制的话,最简单的想法就是把两个序列直接输入一个数组,排序并输出中位数。但是最后一个测试点会“内存超限”。

第二种方法,有序序列归并,每确定一个当前最小的数,就让cnt++,直到cnt增长到中位数的位置。一个小技巧是:为使得代码简洁,可以在两个序列的最后添加一个很大的数INF,以避免其中一个序列已经扫描完还没有到达中位数的情况下访问越界的情况。可惜最后一个测试点也会内存超限。计算一下:4×10​5​​×4=1600000bytes > 内存1.5mb,真正卡的是空间,我们需要优化内存。

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x7fffffff; //int上限 
int main() {
    int n1, n2; 
    scanf("%d", &n1); int s1[n1+1]; s1[n1] = inf; 
    for (int i = 0; i < n1; i++) scanf("%d", &s1[i]);
    scanf("%d", &n2); int s2[n2+1]; s2[n2] = inf; 
    for (int j = 0; j < n2; j++) scanf("%d", &s2[j]);
    //中间位置 当前到达的位置数 
    int mid = (n1 + n2 - 1) / 2, k = 0, t = 0, cnt = 0; 
    while (cnt < mid) {
        if (s1[k] < s2[t]) k++;
        else t++; 
        cnt++;
    }
    if (s1[k] < s2[t]) printf("%d\n", s1[k]); //输出两个序列当前位置较小的元素
    else printf("%d", s2[t]);
    return 0;
}

第三种方法,像上面一样使用双指针,但是第二个数组边读边比较,如果第二个指针t已经等于n2,说明这个序列已经扫描完还没有到达中位数,这时固定a为inf。

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x7fffffff; //int上限 
int main() {
    int n1, n2, a; 
    scanf("%d", &n1); 
    int s1[n1 + 1]; s1[n1] = inf; 
    for (int i = 0; i < n1; i++) scanf("%d", &s1[i]);
    scanf("%d", &n2); 
    //中间位置 当前到达的位置数 
    scanf("%d", &a);
    int mid = (n1 + n2 - 1) / 2, k = 0, t = 0, cnt = 0; 
    while (cnt < mid) {
        if (s1[k] < a) k++;
        else {
            t++; 
            if (t < n2) scanf("%d", &a);
            else if (t == n2) a = inf; //这个序列扫描完了, 用inf作为哨兵
        }
        cnt++;
    }
    if (s1[k] < a) printf("%d\n", s1[k]); //输出两个序列当前位置较小的元素
    else printf("%d", a);
    return 0;
}

(单源最短路Dijkstra+边权+第二标尺(边权)+最短路路径输出) A1030 Travel Plan (30 分) 0.44

A traveler’s map gives the distances between cities along the highways, together with the cost of each highway. Now you are supposed to write a program to help a traveler to decide the shortest path between his/her starting city and the destination. If such a shortest path is not unique, you are supposed to output the one with the minimum cost, which is guaranteed to be unique.

  • Input Specification:
    Each input file contains one test case. Each case starts with a line containing 4 positive integers N, M, S, and D, where N (≤500) is the number of cities (and hence the cities are numbered from 0 to N−1); M is the number of highways; S and D are the starting and the destination cities, respectively. Then M lines follow, each provides the information of a highway, in the format:
    City1 City2 Distance Cost
    
    where the numbers are all integers no more than 500, and are separated by a space.
  • Output Specification:
    For each test case, print in one line the cities along the shortest path from the starting point to the destination, followed by the total distance and the total cost of the path. The numbers must be separated by a space and there must be no extra space at the end of output.
  • Sample Input:
    4 5 0 3
    0 1 1 20
    1 3 2 30
    0 3 4 10
    0 2 2 20
    2 3 1 20
    
  • Sample Output:
    0 2 3 3 40
    

题意:给出地图,做旅游规划,输出起点到目的地到最短路路径、最短路长度、最短路的最小耗费。
思路:Dijkstra的模版题,用一个pre前驱数组存储最短路中每一个结点v的前驱u,起点的前驱是它自己,然后在最短距离更新的同时更新数组c和数组pre。最后递归输出即可。

#include <bits/stdc++.h>
using namespace std;
struct node {
	int to, dis;
};
int n, m, s, v; //城市个数 公路条数 出发地编号 目的地编号
const int maxv = 510, inf = 1000000000;
int vis[maxv], cost[maxv][maxv]; //0 N-1编号 
int d[maxv], c[maxv], pre[maxv]; //点权-花费 
vector<node> G[maxv];

void Dijkstra(int s) {
	for (int i = 0; i < n; i++) pre[i] = i;
	fill(vis, vis + maxv, false); 
	fill(d, d + maxv, inf); d[s] = 0; //将整个d数组初始化为inf 
	fill(c, c + maxv, inf); c[s] = 0; //将整个c数组初始化为inf
	pre[s] = -1; //起点 
	for (int i = 0; i < n; i++) {
		int min = inf, u = -1;
		for (int j = 0; j < n; j++) { //找到未访问顶点中d[j]最小的
			if (vis[j] == false && d[j] < min) { 
				min = d[j];
				u = j;
			}
		}
		if (u == -1) return; //其他的顶点与起点S不连通 
		vis[u] = true;
		for (int k = 0; k < G[u].size(); k++) {
			int v = G[u][k].to;
			if (vis[v] == false) {
				if (d[u] + G[u][k].dis < d[v]) { //最短路径和花费 
					d[v] = d[u] + G[u][k].dis;
					c[v] = c[u] + cost[u][v]; 
					pre[v] = u;
				} else if (d[u] + G[u][k].dis == d[v] && c[u] + cost[u][v] < c[v]) {
					c[v] = c[u] + cost[u][v]; //最短距离相同时看能否使c[v]最优 
					pre[v] = u;
				}
			}
		}
	}
}
void DFS(int s, int v) { //起点s, 终点v 
	if (v == s) {
		printf("%d", s);
		return;
	}
	DFS(s, pre[v]); 
	printf(" %d", v); //输出每一层的顶点号 
}
int main() {
	int c1, c2, dist, co;
	fill(cost[0] , cost[0] + maxv * maxv, 0); //初始化cost 
	scanf("%d%d%d%d", &n, &m, &s, &v);
	for (int i = 0; i < m; i++) {
		scanf("%d%d%d%d", &c1, &c2, &dist, &co);
		G[c1].push_back(node{c2, dist});
		G[c2].push_back(node{c1, dist}); 
		cost[c1][c2] = cost[c2][c1] = co; //无向图 
	} 
	Dijkstra(s);
	DFS(s, v); //输出路径、路径的长度和收费总额
	printf(" %d %d\n", d[v], c[v]);
	return 0;
}

★ A1031 Hello World for U (20 分) 0.37

Given any string of N (≥5) characters, you are asked to form the characters into the shape of U. For example, helloworld can be printed as:

h  d
e  l
l  r
lowo


CodeUp上面做过,解析见以前的文章。

#include <bits/stdc++.h>

int main() {
    char s[100];
    scanf("%s", s);
    int N = strlen(s), spaceNum, n1, n2, n3; //n1n2n3分别是三条边的字符数, 有重叠
    n1 = n3 = (N + 2) / 3, n2 = N - n1 - n3 + 2, spaceNum = n2 - 2;
    for (int i = 0; i < n1 - 1; i++) { //打印除最后一行完的其余行
        printf("%c", s[i]);
        for (int j = 1; j <= spaceNum; j++) printf(" ");
        printf("%c\n", s[N - i - 1]);
    }
    for (int i = 0; i < n2; i++) printf("%c", s[n1 + i - 1]);
    return 0;
}

(静态链表) A1032 Sharing (25 分) 0.26

To store English words, one method is to use linked lists and store a word letter by letter. To save some space, we may let the words share the same sublist if they share the same suffix. For example, loading and being are stored as showed in Figure 1.
fig.jpg
You are supposed to find the starting position of the common suffix (e.g. the position of i in Figure 1).

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains two addresses of nodes and a positive N (≤10​5​​), where the two addresses are the addresses of the first nodes of the two words, and N is the total number of nodes. The address of a node is a 5-digit positive integer, and NULL is represented by −1.
    Then N lines follow, each describes a node in the format:
    Address Data Next
    
    whereAddress is the position of the node, Data is the letter contained by this node which is an English letter chosen from { a-z, A-Z }, and Next is the position of the next node.
  • Output Specification:
    For each case, simply output the 5-digit starting position of the common suffix. If the two words have no common suffix, output -1 instead.
  • Sample Input 1:
    11111 22222 9
    67890 i 00002
    00010 a 12345
    00003 g -1
    12345 D 67890
    00002 n 00003
    22222 B 23456
    11111 L 00001
    23456 e 67890
    00001 o 00010
    
  • Sample Output 1:
    67890
    
  • Sample Input 2:
    00001 00002 4
    00001 a 10001
    10001 s -1
    00002 a 10002
    10002 t -1
    

用链表存储两个单词,其中两个单词相同的后缀(suffix)共用相同的结点。一开始我是这么理解的,于是有了以下的代码。结点个数少,用静态链表;然后避免出现无效结点,方便操作地址,用List1、List2记录有效结点的地址和数量(这其实是我从乙级的B1025学到的,小心谨慎一点没错吧);接着为了求共同后缀的第一个结点, 我将两个地址表反转,对齐表尾开始查找,值相同则继续, 值不同则说明超出了共同后缀的前界了,退出并打印目标结点。可以通过两个样例,但结果是,最后一个测试点没有通过……

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
struct node {
    char c;
    int next;
} words[maxn];
int List1[maxn], L1 = 0, List2[maxn], L2 = 0;

int main() {
    int first1, first2, n, addr;
    scanf("%d%d%d", &first1, &first2, &n);
    for (int i = 0; i < n; i++) {
        cin >> addr;
        cin >> words[addr].c >> words[addr].next;
    }
    while (first1 != -1) { //记录单词1有效结点的地址
        List1[L1++] = first1;
        first1 = words[first1].next;
    }
    while (first2 != -1) { //记录单词2有效结点的地址
        List2[L2++] = first2;
        first2 = words[first2].next;
    } //比较后缀, 从后面往前比较更加简单
    reverse(List1, List1 + L1);
    reverse(List2, List2 + L2);
    int suf = -1;
    for (int i = 0; i < L1 && i < L2; i++) {
        if (words[List1[i]].c == words[List2[i]].c) suf = List1[i];
        else break;
    }
    if (suf == -1) printf("-1\n");
    else printf("%05d\n", suf);
    return 0;
}

于是我修改了代码,题意确实很容易让人误解,它说的是共同的后缀starting position of the common suffix,但是存储单词时,相同的字符且共用相同的地址(地址不同不符合条件,见样例二)才是common,才是共用结点,而并不是严格的后缀。只是在某一段区间两个串/结点的值是相同的(严格的说必须是地址相同),然后后面的值又会不同。于是改了两行代码,改成从后面往前面找到第一个地址相同的结点(值自然相同),即首个共用结点,地址不同的话并不会直接退出,结果通过了。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
struct node {
    char c;
    int next;
} words[maxn];
int List1[maxn], L1 = 0, List2[maxn], L2 = 0;

int main() {
    int first1, first2, n, addr;
    scanf("%d%d%d", &first1, &first2, &n);
    for (int i = 0; i < n; i++) {
        cin >> addr;
        cin >> words[addr].c >> words[addr].next;
    }
    while (first1 != -1) { //记录单词1有效结点的地址
        List1[L1++] = first1;
        first1 = words[first1].next;
    }
    while (first2 != -1) { //记录单词2有效结点的地址
        List2[L2++] = first2;
        first2 = words[first2].next;
    } //比较后缀, 从后面往前比较更加简单
    reverse(List1, List1 + L1);
    reverse(List2, List2 + L2);
    int suf = -1; //共同的结点不仅会有相同的值, 还应该有相同的地址
    for (int i = 0; i < L1 && i < L2; i++) 
        if (List1[i] == List2[i]) suf = List1[i]; //地址相同则值也相同
    if (suf == -1) printf("-1\n");
    else printf("%05d\n", suf);
    return 0;
}

翻了下书,发现了更加简单的方法,仍旧是静态链表,只不过把题意理解得更加清晰了。给出两条链表的首地址以及若干个结点的地址、数据、下一个结点的地址,求两个结点首个共用结点的地址,没有共用结点则输出-1。

这里用flag表示第一条链表有没有该结点。遍历第一条链表,将访问过的结点的flag都标记为true,当遍历第二条结点的时候,如果遇到了true的结点就输出并结束程序,没有遇到就输出-1。这就跳出了比较字符的值的想法,简洁有力,而且节省时间。

#include <cstdio>
using namespace std;
struct NODE {
    char key;
    int next;
    bool flag;
}node[100000];
int main() {
    int s1, s2, n, a, b;
    scanf("%d%d%d", &s1, &s2, &n);
    char data;
    for(int i = 0; i < n; i++) {
        scanf("%d %c %d", &a, &data, &b);
        node[a] = {data, b, false};
    }
    for(int i = s1; i != -1; i = node[i].next)
        node[i].flag = true;
    for(int i = s2; i != -1; i = node[i].next) {
        if(node[i].flag == true) {
            printf("%05d", i);
            return 0;
        }
    }
    printf("-1");
    return 0;
}

★★★(贪心) A1033 To Fill or Not to Fill (25 分) 0.28

With highways available, driving a car from Hangzhou to any other city is easy. But since the tank capacity of a car is limited, we have to find gas stations on the way from time to time. Different gas station may give different price. You are asked to carefully design the cheapest route to go.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains 4 positive numbers: C​max​​ (≤ 100), the maximum capacity of the tank; D (≤30000), the distance between Hangzhou and the destination city; D​avg​​ (≤20), the average distance per unit gas that the car can run; and N (≤ 500), the total number of gas stations. Then N lines follow, each contains a pair of non-negative numbers: P​i​​, the unit gas price, and D​i​​ (≤D), the distance between this station and Hangzhou, for i=1,⋯,N. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, print the cheapest price in a line, accurate up to 2 decimal places. It is assumed that the tank is empty at the beginning. If it is impossible to reach the destination, print The maximum travel distance = X where X is the maximum possible distance the car can run, accurate up to 2 decimal places.
  • Sample Input 1:
    50 1300 12 8
    6.00 1250
    7.00 600
    7.00 150
    7.10 0
    7.20 200
    7.50 400
    7.30 1000
    6.85 300
    
  • Sample Output 1:
    749.17
    
  • Sample Input 2:
    50 1300 12 2
    7.10 0
    7.00 600
    
  • Sample Output 2:
    The maximum travel distance = 1200.00
    

提示:该题目所要解决的问题是:给定若干加油站信息,问能否驾驶汽车行驶一定的距离。如果能够行驶完全程,则计算最小花费。若不能行驶完全程,则最远能够行驶多长距离

拿到这一题,首先判断汽车是否能够行驶到终点。什么情况下汽车无法行驶到终点呢?两种情况:起点根本就没有加油站,汽车无法启动;或者中途两个加油站之间的距离大于加满油后汽车能够行驶的最大距离。前者汽车行驶的最大距离为0.00,而后者最大距离为当前加油站的距离加上在这个加油站加满油后能够行驶的最大距离。在这里,需要将加油站按到杭州的距离从小到大排序

接下来在能够行驶到终点的情况下计算最小花费。我们首先从路程来考虑,如果在路上,我们能够使用最便宜的汽油,当然就在那个加油站加油了。所以从起点开始遍历每个加油站。假设遍历到了第i个加油站,我们现在来判断在加油站i应该加多少油

  • 在第i个加油站的作用范围内寻找有没有更为便宜的加油站,如果有,则下次使用这个加油站的油j:
    • 如果可以直接到达j,就不加油;
    • 不然应该在站点i加一定的油行驶到那个加油站j,即touch=nodes[j].dis。
  • 如果没有找到更为便宜的加油站则可以在第i个加油站加满油,然后行驶到下一个加油站再进行判断。
#include <cstdio>
#include <algorithm>
using namespace std;
typedef struct gasStation {
	double p, d, s; //p: 价格,d:据出发点距离,s:离上个站点的间距
} gs;
bool cmp(gs &a, gs &b) {
	return a.d < b.d; 
}

int main() {
	double C, D, Davg; 
	int n;  //从杭州到目的地的加油站数目 
	scanf("%lf%lf%lf%d", &C, &D, &Davg, &n);
    gs gstat[n + 1];
    double maxl = C * Davg; //满油后最大行驶距离 
    for (int i = 0; i < n; i++) {
        scanf("%lf%lf", &gstat[i].p, &gstat[i].d);
    }
    gstat[n].p = 0; //目的地 
    gstat[n].d = D;
    sort(gstat, gstat + n + 1, cmp);
    for (int i = 1; i <= n; i++) {
        gstat[i].s = gstat[i].d - gstat[i - 1].d;
    }
    /* 如果第一个加油站不在杭州, 没法加油 */
    if (gstat[0].d > 0) {
        printf("The maximum travel distance = 0.00\n");
        return 0; 
    } else {
        for (int i = 1; i <= n; i++) {
            if (gstat[i].s > maxl) { //有站点不可达 
                printf("The maximum travel distance = %.2f\n", gstat[i - 1].d + maxl);
                return 0; 
            }
        }
    }
    double cost = 0, nowTank = 0;
    int nowSta = 0, signal = 1; 
    for (int i = 0; i < n; i++) {
         if (i != nowSta) continue;
         for (int j = i + 1; j <= n && (gstat[j].d - gstat[i].d) <= maxl; j++) {
             if (gstat[j].p < gstat[nowSta].p) {
                 if (nowTank < (gstat[j].d - gstat[nowSta].d) / Davg) {
                     cost += gstat[nowSta].p * ((gstat[j].d - gstat[nowSta].d) / Davg - nowTank);
                     nowTank = 0;
                 } else nowTank -= (gstat[j].d - gstat[nowSta].d) / Davg;
                 nowSta = j;
                 signal = 1;
                 break;
             } else signal = 0;
         }
         if (!signal) {
             cost += gstat[i].p * (C - nowTank);
             nowTank = C - (gstat[i + 1].s / Davg);
             nowSta++;
         }
     } 
     printf("%.2f\n", cost);
     return 0;
}

★★★(DFS) A1034 Head of a Gang (30 分) 0.30

One way that the police finds the head of a gang is to check people’s phone calls. If there is a phone call between A and B, we say that A and B is related. The weight of a relation is defined to be the total time length of all the phone calls made between the two persons. A “Gang” is a cluster of more than 2 persons who are related to each other with total relation weight being greater than a given threthold K. In each gang, the one with maximum total weight is the head. Now given a list of phone calls, you are supposed to find the gangs and the heads.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains two positive numbers N and K (both less than or equal to 1000), the number of phone calls and the weight threthold, respectively. Then N lines follow, each in the following format:
    Name1 Name2 Time
    
    where Name1 and Name2 are the names of people at the two ends of the call, and Time is the length of the call. A name is a string of three capital letters chosen from A-Z. A time length is a positive integer which is no more than 1000 minutes.
  • Output Specification:
    For each test case, first print in a line the total number of gangs. Then for each gang, print in a line the name of the head and the total number of the members. It is guaranteed that the head is unique for each gang. The output must be sorted according to the alphabetical order of the names of the heads.
  • Sample Input 1:
    8 59
    AAA BBB 10
    BBB AAA 20
    AAA CCC 40
    DDD EEE 5
    EEE DDD 70
    FFF GGG 30
    GGG HHH 20
    HHH FFF 10
    
  • Sample Output 1:
    2
    AAA 3
    GGG 3
    
  • Sample Input 2:
    8 70
    AAA BBB 10
    BBB AAA 20
    AAA CCC 40
    DDD EEE 5
    EEE DDD 70
    FFF GGG 30
    GGG HHH 20
    HHH FFF 10
    
  • Sample Output 2:
    0
    
#include <bits/stdc++.h>
using namespace std;

const int maxv = 2010, inf = 0x7fffffff; //N<=1000条通话记录, 最大2000人 
//姓名->编号, 比起自己使用hash函数方便, 节省空间
map<string, int> stringToInt; 
//编号->姓名, 比起自己用string数组或二维字符数组, 更方便按字典序输出结果 
map<int, string> intToString; 
map<string, int> Gang; //head->人数
//邻接矩阵中存储边权, weight中存储点权, 点权为与该点相连的所有边的边权和 
int G[maxv][maxv] = {0}, weight[maxv] = {0}; 
int n, k, numPerson = 0; //边数n, 下限k, 总人数numPerson 
bool vis[maxv] = {false};

bool DFS(int nowVisit, int &head, int &number, int &totalValue) {
	vis[nowVisit] = true;
	number++; //计算连通块中的成员结点个数
	if (weight[nowVisit] > weight[head]) {
		head = nowVisit; //当前访问的点权大于头目的点权, 更新头目 
	}
	for (int i = 0; i < numPerson; i++) {
		if (G[nowVisit][i] > 0) { //如果nowVisit能到达i  
		    totalValue += G[nowVisit][i]; //连通块的总边权增加该边权
			G[nowVisit][i] = G[i][nowVisit] = 0; //删除这条边, 防止回头 
			if (vis[i] == false) {
				DFS(i, head, number, totalValue);
			}
		}
	}
} 
//遍历整个图, 获取每个连通块的信息 
void DFSTrave() {
	for (int i = 0; i < numPerson; i++)	{
		if (vis[i] == false) {
			int head = i, number = 0, totalValue = 0; //头目、成员数、总边权 
		    DFS(i, head, number, totalValue);
			if (number > 2 && totalValue > k) {
				Gang[intToString[head]] = number; //head和人数 
			} 
		}
		 
	}
}
//change函数返回姓名对应的编号, 用每个人出现的次序作为编号, 方便统计点权 
int change(string s) {
	if (stringToInt.find(s) != stringToInt.end()) { //已经出现过 
		return stringToInt[s]; //返回编号 
	} else {
		stringToInt[s] = numPerson; //s的编号为numPerson
		intToString[numPerson] = s; //numPerson对应姓名s
		return numPerson++;  //总人数+1 
	}
} 

int main() {
    scanf("%d%d", &n, &k);
    string s1, s2;
    int w;
    for (int i = 0; i < n; i++) {
    	cin >> s1 >> s2 >> w; //输入边的两个端点和边权 
    	int id1 = change(s1), id2 = change(s2);
    	weight[id1] += w; //id1的点权加w 
    	weight[id2] += w; //id2的点权加w  
		G[id1][id2] += w; //边id1->id2的边权加w 
		G[id2][id1] += w; //边id2->id1的边权加w 
	}
	DFSTrave(); //遍历整个图的连通块
	cout << Gang.size() << endl; //Gang的个数
	for (auto it : Gang) { //遍历所有Gang 
		cout << it.first << " " << it.second << endl; //输出信息 
	} 
	return 0;
}

A1035 Password (20 分) 0.30

修改密码。没有密码需要修改时,需要注意输出的单复数形式

#include <bits/stdc++.h>
struct account {
    char name[15], password[15];
    int flag;  //1代表修改了
    account() {flag = 0; }
} p[1010];
int main() {
    int n, m = 0;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%s%s", p[i].name, p[i].password);
        for (int j = 0; p[i].password[j]; j++) {
            switch (p[i].password[j]) {
                case '1':
                    p[i].password[j] = '@'; m += p[i].flag ? 0 : 1; 
					p[i].flag = 1; break;
                case 'l':
                    p[i].password[j] = 'L'; m += p[i].flag ? 0 : 1; 
					p[i].flag = 1; break;
                case '0':
                    p[i].password[j] = '%'; m += p[i].flag ? 0 : 1; 
					p[i].flag = 1; break;
                case 'O':
                    p[i].password[j] = 'o'; m += p[i].flag ? 0 : 1; 
					p[i].flag = 1; break;
                default: break;
            }
        }
    }
    if (!m) {
        if (n == 1) printf("There is 1 account and no account is modified\n");
        else printf("There are %d accounts and no account is modified\n", n);
    } else {
        printf("%d\n", m);
        for (int i = 0; i < n; i++) {
            if (p[i].flag == 1) printf("%s %s\n", p[i].name, p[i].password);
        }
    }
}

A1036 Boys vs Girls (25 分) 0.43

#include <bits/stdc++.h>
using namespace std;
struct person {
	string name, id;
	char gender;
	int grade;
	person() {}
	person(int g) {	grade = g; } 
} m(100), f(0), temp; //m存放成绩最低的男同学, f存放成绩最高的女同学 

int main() {
	int n, numM = 0, numF = 0; //人数 
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		cin >> temp.name >> temp.gender >> temp.id >> temp.grade;
		if (temp.gender == 'M' && temp.grade < m.grade) {
			m = temp; numM++;
		} else if (temp.gender == 'F' && temp.grade > f.grade) {
			f = temp; numF++;
		}
	}
	if (numF) cout << f.name << " " << f.id << endl;
	else printf("Absent\n");
	if (numM) cout << m.name << " " << m.id << endl;
	else printf("Absent\n");
	if (!numF || !numM) printf("NA\n");
	else printf("%d\n", f.grade - m.grade);
	return 0;
}

A1037 Magic Coupon (25 分) 0.39

这是一家奇特的商店,优惠券可正可负,福利品价值同样可正可负。其问题可以等价于两个整数集合,从中分别挑选相同数量的元素进行一对一相乘,问能得到的最大结果是多少。

贪心策略如下:

  • 将每个集合分成正数和负数来考虑,0不被考虑;
  • 对每个集合的正数集合从大到小排序,负数集合从小到大进行排序;
  • 对每个集合的正数集进行相同位置上的正数与正数相乘,负数集进行相同位置上的负数与负数相乘。累加结果求和即可。
  • 原因在于,大的正数乘以大的正数得到的乘积更大,小的负数乘以小的负数得到的乘积更大。

题目中说,it is guaranteed that all the numbers will not exceed 2​30​​,但是保险起见还是将结果定义为long long型。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
bool cmp(int a, int b) {
    return abs(a) > abs(b); //正数从大到小排列, 负数从小到大排列
}

int main() {
    int nc, np, t;
    scanf("%d", &nc);
    int Pc[nc] = {0}, Nc[nc] = {0}, PcLen = 0, NcLen = 0; //正值券 负值券 
    for (int i = 0; i < nc; i++) {
        scanf("%d", &t);
        if (t >= 0) Pc[PcLen++] = t;
        else Nc[NcLen++] = t;
    }
    scanf("%d", &np);
    int Pp[np] = {0}, Np[np] = {0}, PpLen = 0, NpLen = 0; //正值产品 负值产品
    for (int i = 0; i < np; i++) {
        scanf("%d", &t);
        if (t >= 0) Pp[PpLen++] = t;
        else Np[NpLen++] = t;
    }
    sort(Pc, Pc + PcLen, cmp);
    sort(Nc, Nc + NcLen, cmp);
    sort(Pp, Pp + PpLen, cmp);
    sort(Np, Np + NpLen, cmp);	
	ll max = 0;
	for (int i = 0; i < PcLen && i < PpLen; i++) 
		max += (ll)(Pc[i] * Pp[i]);
	for (int i = 0; i < NcLen && i < NpLen; i++) 
		max += (ll)(Nc[i] * Np[i]);
	printf("%lld\n", max);
    return 0;
}

(字符串贪心) A1038 Recover the Smallest Number (30 分) 0.33

Given a collection of number segments, you are supposed to recover the smallest number from them. For example, given { 32, 321, 3214, 0229, 87 }, we can recover many numbers such like 32-321-3214-0229-87 or 0229-32-87-321-3214 with respect to different orders of combinations of these segments, and the smallest number is 0229-321-3214-32-87.

  • Input Specification:
    Each input file contains one test case. Each case gives a positive integer N (≤10​4​​) followed by N number segments. Each segment contains a non-negative integer of no more than 8 digits. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, print the smallest number in one line. Notice that the first digit must not be zero.
  • Sample Input:
    5 32 321 3214 0229 87
    
  • Sample Output:
    22932132143287
    

给出若干个含有前导0的字符串,将它们按照某个顺序拼接,得到最小的数。

  • 这是道贪心问题,对字符串s1和s2,如果s1+s2<s2+s1,那么把s1放在s2的前面,否则把s2放在s1的前面;
  • 用string会使代码简洁明了;
  • 字符串第一个数字可能会是前导0,需要将这些前导0去除, 但是至少要保留一位。一种想法可能是string转换为数字,使用stoi,但是这会溢出,因此,我们采取直接删除前导0的做法。
    //input
    3 0 00 000
    //output
    0
    //input 
    3 00 0020 000
    //output
    20
    
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10010;
string seg[maxn];

bool cmp(string s1, string s2) {
    return s1 + s2 < s2 + s1; //两个数字字符串拼接起来字典序越小的排在越前
}

int main() {
    int n; scanf("%d", &n);
    for (int i = 0; i < n; i++) 
        cin >> seg[i];
    sort(seg, seg + n, cmp);
    string ans = "";
    for (int i = 0; i < n; i++)   
    	ans += seg[i]; 
    //去除前导0, 但最后至少要保留一位
	while (ans.size() != 1 && ans[0] == '0') ans.erase(ans.begin()); 
    cout << ans << endl;
    return 0;
}

A1039 Course List for Student (25 分) 0.30

CodeUp上面做过的题目,当初没有用stl的时候让我要死要活,现在用了string、unordered_map后效率提高了很多。不过最后一组代码有时候还是可能超时……不过反正我是通过了。 翻了下书发现书上写的这道题限时200ms,然而PAT上面的时间限制: 600 ms……总之不同的时限有不同的方法。如果只有200ms,那就只能手动字符串哈希,然后用vector数组(用二维数组可能导致内存超限),不用cin/cout。

回到这个题目,它让我们输出的是每个查询的学生的课程表,但是它给我们的数据是每门课程的学生名单,因此我们需要在输入时就进行预处理,这样输出时就很方便了。然后题目要求:finally the indices of the courses in increasing order,这给了我们暗示,在插入每个学生需要上的课程时可以嵌套使用set,自动递增排序。当然,这样我们就会对每个学生上的课程进行排序,有时候不必要,万一查询的人数比较少呢?所以也可以用map嵌套vector,当需要输出的时候再进行排序。

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, k, id, num; 
    scanf("%d%d", &n, &k);
    unordered_map<string, set<int> > stuCourseList; //学生的课程表
    string t;
    while (k--) {
        scanf("%d%d", &id, &num); //课程id, 课程学生数目
        while (num--) {
            cin >> t;
            stuCourseList[t].insert(id); //该学生需要上的课程
        }
    }
    while (n--) {
        cin >> t;
        if (stuCourseList.find(t) != stuCourseList.end()) { //有这个学生的课程表信息
            cout << t; printf(" %d", stuCourseList[t].size());
            for (auto it : stuCourseList[t]) cout << " " << it;
            printf("\n");
        } else cout << t << " 0" << endl; //没有这个学生的课程表信息
    }
    return 0;
}

(二维动态规划) A1040 Longest Symmetric String(25 分) 0.31

Given a string, you are supposed to output the length of the longest symmetric sub-string. For example, given Is PAT&TAP symmetric?, the longest symmetric sub-string is s PAT&TAP s, hence you must output 11.

  • Input Specification:
    Each input file contains one test case which gives a non-empty string of length no more than 1000.
  • Output Specification:
    For each test case, simply print the maximum length in a line.
  • Sample Input:
    Is PAT&TAP symmetric?
    
  • Sample Output:
    11
    

注意点:用cin和getline输入一行。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int dp[maxn][maxn];
/* dp[i][j]表示s[i]到s[j]所表示的字串串是否是回⽂文字串串。只有0和1
1 当s[i] == s[j] : dp[i][j] = dp[i+1][j-1]
2 当s[i] != s[j] : dp[i][j] =0
3 边界:dp[i][i] = 1, dp[i][i+1] = (s[i] == s[i+1]) ? 1 : 0
*/
int main() {
    string s;
    getline(cin, s);
    int ans = 1;
    /* 首先初始化dp[i][i] = 1, dp[i][i+1],把⻓长度为1和2的都初始化好,
    然后从L = 3开始一直到L <= len 根据动态规划的递归⽅方程来判断 */
    memset(dp, 0, sizeof(dp)); //dp数组初始化为0
    for (int i = 0; i < s.size(); i++) {
        dp[i][i] = 1;
        if (i < s.size() - 1) {
            if (s[i] == s[i + 1]) {
                dp[i][i + 1] = 1;
                ans = 2; //初始化时注意最长回文子串的长度
            }
        }
    }
    //状态转移方程
    for (int L = 3; L <= s.size(); L++) {
        for (int i = 0; i + L - 1 < s.size(); i++) {
            int j = i + L - 1;
            if (s[i] == s[j] && dp[i + 1][j - 1] == 1) {
                dp[i][j] = 1;
                ans = L;
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

A1041 Be Unique (20 分) 0.42

火星彩票,第一个赌到只出现一次的数字的人赢。CodeUP上面做过的题目。

#include <cstdio>
const int maxn = 10010;
int hashTable[maxn] = {0};

int main() {
    int n; scanf("%d", &n);
    int a[n], f = 1; 
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        hashTable[a[i]]++;
    }
    for (int i = 0; i < n; i++) {
        if (hashTable[a[i]] == 1) {
            printf("%d\n", a[i]);
            f = 0; break;
        }
    }
    if (f) printf("None\n");
    return 0;
}

A1042 Shuffling Machine (20 分) 0.40

A given order is a permutation of distinct integers in[1, 54]. If the number at the i-th position is j, it means to move the card from position i to position j. 这是这道题中最关键的部分,这种操作直接明确了每个位置的牌在操作后的位置,所以一个start数组和end数组,每次将start数组中的牌存放到end数组中的指定位置去,然后用end数组覆盖start数组开启下一轮操作。最终start数组就是最后的排序。

#include <cstdio>
const int N = 54;
char mp[5] = {'S', 'H', 'C', 'D', 'J'}; //牌的编号与花色的对应关系
int start[N+1], end[N+1], next[N+1]; //next数组存放每个位置上的牌在操作后的位置

int main() {
    int K;
    scanf("%d", &K);
    for (int i = 1; i <= N; i++) start[i] = i; //初始化牌的编号
    for (int i = 1; i <= N; i++) scanf("%d", &next[i]); 
    while (K--) { //K次操作
        for (int i = 1; i <= N; i++) end[next[i]] = start[i];
        for (int i = 1; i <= N; i++) start[i] = end[i];
    }
    for (int i = 1; i <= N; i++) {
        if (i > 1) printf(" ");
        start[i]--;
        printf("%c%d", mp[start[i] / 13], start[i] % 13 + 1); //输出结果
    }
    return 0;
}

★★ (二叉排序树建树+二叉树先序后序遍历) A1043 Is It a Binary Search Tree (25 分) 0.35

A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties:

The left subtree of a node contains only nodes with keys less than the node’s key.
The right subtree of a node contains only nodes with keys greater than or equal to the node’s key.
Both the left and right subtrees must also be binary search trees.

If we swap the left and right subtrees of every node, then the resulting tree is called the Mirror Image of a BST.
Now given a sequence of integer keys, you are supposed to tell if it is the preorder traversal sequence of a BST or the mirror image of a BST.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains a positive integer N (≤1000). Then N integer keys are given in the next line. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, first print in a line YES if the sequence is the preorder traversal sequence of a BST or the mirror image of a BST, or NO if not. Then if the answer is YES, print in the next line the postorder traversal sequence of that tree. All the numbers in a line must be separated by a space, and there must be no extra space at the end of the line.
  • Sample Input 1:
    7
    8 6 5 7 10 8 11
    
  • Sample Output 1:
    YES
    5 7 6 8 11 10 8
    
  • Sample Input 2:
    7
    8 10 11 8 6 7 5
    
  • Sample Output 2:
    YES
    11 8 10 7 5 6 8
    
  • Sample Input 3:
    7
    8 6 8 5 10 9 11
    
  • Sample Output 3:
    NO
    

题意:给出一个整数键值序列,判断这是否是一棵二叉搜索树或者其镜像的前序遍历序列。
方法一:每输入一个整数,将其插入空树中,形成一棵二叉排序树。然后先对其进行先序遍历,如果给出的整数序列就是这棵二叉排序树的先序序列,就输出其后序序列;不然就进行镜像先序遍历,然后输出其镜像后序序列;不然就输出NO。

#include <bits/stdc++.h>
using namespace std;
struct node {
    int data;
    node *left, *right;
};
node *newNode(int v) {
	node *root = new node;
	root->data = v;
	root->left = root->right = NULL;
	return root; 
}
node *insert(node *root, int v) {
	if (root == NULL) {
		root = newNode(v);
	} else { //小于插入左子树, 其他插入右子树 
		if (v < root->data) root->left = insert(root->left, v);
		else if (v >= root->data) root->right = insert(root->right, v); 
	}
	return root;
}
vector<int> pre, post, preImag, postImag;
int n; //树结点个数 
void preorder(node *root) {
	if (root) { //先序遍历二叉树, 存入pre 
		pre.push_back(root->data);
		preorder(root->left);
		preorder(root->right);
	}
}
void postorder(node *root) {
	if (root) { //后序遍历二叉树, 存入post 
		postorder(root->left);
		postorder(root->right);
		post.push_back(root->data);
	}
}
void preorderImag(node *root) {
    if (root) { //镜像先序遍历二叉树, 存入preImag
		preImag.push_back(root->data);
		preorderImag(root->right);
		preorderImag(root->left);
	}
}
void postorderImag(node *root) {
	if (root) { //镜像后序遍历二叉树, 存入postImag 
		postorderImag(root->right);
		postorderImag(root->left);
		postImag.push_back(root->data);
	}	
}
bool isSame(vector<int> &a, vector<int> &b) {
	for (int i = 0; i < a.size(); i++) //比较两个序列是否相同 
		if (a[i] != b[i]) return false;
	return true;
}
void trace(vector<int> &a) {
	printf("YES\n"); //如果两个序列相同, 就输出它的树的后序遍历序列 
	for (int i = 0; i < a.size(); i++) {
		if (i > 0) printf(" ");
		printf("%d", a[i]); 
	}
	printf("\n");
}
int main() {
	scanf("%d", &n);
	vector<int> origin(n);
	node *root = NULL; //插入结点建树 
	for (int i = 0; i < n; i++) {
		scanf("%d", &origin[i]);
		root = insert(root, origin[i]);
	}
	preorder(root);
	if (isSame(pre, origin)) {
		postorder(root);
		trace(post); 
		return 0;		
	} 
	preorderImag(root);
	if (isSame(preImag, origin)) {
		postorderImag(root);
		trace(postImag);
		return 0;
	}
	printf("NO\n");
	return 0;
}

不建树的做法。先假设这就是二叉排序树。一开始isMirror为False,根据二叉排序树的性质(前序序列中,根结点之后小于根结点的部分是左子树,大于等于根结点的部分是右子树),将已知的前序转为后序,转换后发现后序数组长度不为n,则设isMirror为true,清空后序数组,重新根据镜面二叉树的性质转换一次。同样长度不等于n,就输出NO。

#include <bits/stdc++.h> 
using namespace std;
bool isMirror;
vector<int> pre, post;
void getpost(int root, int tail) {
    if(root > tail) return ;
	int i = root + 1, j = tail;
	if(!isMirror) {
	    while(i <= tail && pre[root] > pre[i]) i++;
        while(j > root && pre[root] <= pre[j]) j--;
	} else {
        while(i <= tail && pre[root] <= pre[i]) i++;
		while(j > root && pre[root] > pre[j]) j--;
	}
	if(i - j != 1) return ;
    getpost(root + 1, j);
	getpost(i, tail);
	post.push_back(pre[root]);
}
int main() {
	int n;
	scanf("%d", &n);
	pre.resize(n);
	for(int i = 0; i < n; i++)
	    scanf("%d", &pre[i]);
	getpost(0, n - 1);
	if(post.size() != n) {
        isMirror = true;
		post.clear();
		getpost(0, n - 1);
	}
	if(post.size() == n) {
        printf("YES\n%d", post[0]);
		for(int i = 1; i < n; i++)
		    printf(" %d", post[i]);
	} else printf("NO"); 
	return 0;
}

★★(前缀和+二分) A1044 Shopping in Mars (25 分) 0.30

Shopping in Mars is quite a different experience. The Mars people pay by chained diamonds. Each diamond has a value (in Mars dollars M$). When making the payment, the chain can be cut at any position for only once and some of the diamonds are taken off the chain one by one. Once a diamond is off the chain, it cannot be taken back. For example, if we have a chain of 8 diamonds with values M$3, 2, 1, 5, 4, 6, 8, 7, and we must pay M$15. We may have 3 options:

Cut the chain between 4 and 6, and take off the diamonds from the position 1 to 5 (with values 3+2+1+5+4=15).
Cut before 5 or after 6, and take off the diamonds from the position 4 to 6 (with values 5+4+6=15).
Cut before 8, and take off the diamonds from the position 7 to 8 (with values 8+7=15).

Now given the chain of diamond values and the amount that a customer has to pay, you are supposed to list all the paying options for the customer.

If it is impossible to pay the exact amount, you must suggest solutions with minimum lost.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains 2 numbers: N (≤10​5​​), the total number of diamonds on the chain, and M (≤10​8​​), the amount that the customer has to pay. Then the next line contains N positive numbers D​1​​⋯D​N​​ (D​i​​≤10​3​​ for all i=1,⋯,N) which are the values of the diamonds. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, print i-j in a line for each pair of i ≤ j such that Di + … + Dj = M. Note that if there are more than one solution, all the solutions must be printed in increasing order of i.
    If there is no solution, output i-j for pairs of i ≤ j such that Di + … + Dj >M with (Di + … + Dj −M) minimized. Again all the solutions must be printed in increasing order of i.
    It is guaranteed that the total value of diamonds is sufficient to pay the given amount.
  • Sample Input 1:
    16 15
    3 2 1 5 4 6 8 7 16 10 15 11 9 12 14 13
    
  • Sample Output 1:
    1-5
    4-6
    7-8
    11-11
    
  • Sample Input 2:
    5 13
    2 4 5 7 9
    
  • Sample Output 2:
    2-4
    4-5
    

这道题以后还会再做的。

#include <bits/stdc++.h>
const int maxn = 100010;
int presum[maxn], nears = 0x7fffffff;
int upper_bound(int L, int R, int x) {
    int left = L, right = R, mid;
    while (left < right) {
        mid = (left + right) / 2;
        if (presum[mid] > x) right = mid;
        else left = mid + 1;
    }
    return left;
}
int main() {
    int n, s;
    scanf("%d%d", &n, &s);
    presum[0] = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &presum[i]);
        presum[i] += presum[i - 1]; //求presum[i]
    }
    //找到sum[j]-sum[i-1]=s, 即i-j区间
    for (int i = 1; i <= n; i++) { //找到sum[j]=sum[i-1]+s
        int j = upper_bound(i, n + 1, presum[i - 1] + s);
        if (presum[j - 1] - presum[i - 1] == s) { //查找成功
            nears = s; //最接近s的值就是s
            break; 
        } else if (j <= n && presum[j] - presum[i - 1] < nears) {
            //存在大于s的解并且小于nears
            nears = presum[j] - presum[i - 1]; //更新当前nears
        }
    }
    for (int i = 1; i <= n; i++) {
        int j = upper_bound(i, n + 1, presum[i - 1] + nears); //求右端点
        if (presum[j - 1] - presum[i - 1] == nears) { //查找成功
            printf("%d-%d\n", i, j - 1); //输出左端点和右端点
        }
    }
    return 0;
}

(dp, LIS, LCS) A1045 Favorite Color Stripe (30 分) 0.37

Eva is trying to make her own color stripe out of a given one. She would like to keep only her favorite colors in her favorite order by cutting off those unwanted pieces and sewing the remaining parts together to form her favorite color stripe.

It is said that a normal human eye can distinguish about less than 200 different colors, so Eva’s favorite colors are limited. However the original stripe could be very long, and Eva would like to have the remaining favorite stripe with the maximum length. So she needs your help to find her the best result.

Note that the solution might not be unique, but you only have to tell her the maximum length. For example, given a stripe of colors {2 2 4 1 5 5 6 3 1 1 5 6}. If Eva’s favorite colors are given in her favorite order as {2 3 1 5 6}, then she has 4 possible best solutions {2 2 1 1 1 5 6}, {2 2 1 5 5 5 6}, {2 2 1 5 5 6 6}, and {2 2 3 1 1 5 6}.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains a positive integer N (≤200) which is the total number of colors involved (and hence the colors are numbered from 1 to N). Then the next line starts with a positive integer M (≤200) followed by M Eva’s favorite color numbers given in her favorite order. Finally the third line starts with a positive integer L (≤10​4​​) which is the length of the given stripe, followed by L colors on the stripe. All the numbers in a line a separated by a space.
  • Output Specification:
    For each test case, simply print in a line the maximum length of Eva’s favorite stripe.
  • Sample Input:
    6
    5 2 3 1 5 6
    12 2 2 4 1 5 5 6 3 1 1 5 6
    
  • Sample Output:
    7
    
#include <bits/stdc++.h>
using namespace std;
const int maxc = 210; //最大颜色数
const int maxn = 10010; //最大L
int HashTable[maxc]; //将喜欢的颜色序列映射为递增序列, 不喜欢的颜色映射为-1
int A[maxn], dp[maxn]; //最长不下降子序列的原数组和DP数组
int main() {
    int n, m, x;
    scanf("%d%d", &n, &m); 
    memset(HashTable, -1, sizeof(HashTable));
    for (int i = 0; i < m; i++) {
        scanf("%d", &x);
        HashTable[x] = i; //将喜欢的颜色按照顺序映射到递增序列0,1,2,3...
    }
    int L, num = 0; //num存放颜色序列中Eva喜欢的颜色总数
    scanf("%d", &L);
    for (int i = 0; i < L; i++) {
        scanf("%d", &x);
        if (HashTable[x] >= 0) A[num++] = HashTable[x]; //如果是喜欢的颜色, 加入到A数组中
    }
    int ans = -1;
    for (int i = 0; i < num; i++) {
        dp[i] = 1;
        for (int j = 0; j < i; j++) { //LIS问题的模板
            if (A[j] <= A[i] && dp[i] < dp[j] + 1) dp[i] = dp[j] + 1;
        }
        ans = max(ans, dp[i]);
    }
    printf("%d\n", ans);
    return 0;
}

A1046 Shortest Distance (20 分)

It is guaranteed that the total round trip distance is no more than 10​7​​

我在Code UP上面做过这道题,原先的代码和我下面写的代码都在PAT的最后一个测试点运行超时,部分正确。因为有[3,10​5​​]个整数和≤10​4次查询,如果采取遍历数组的方法,极端情况就是109次操作,在200ms是不可承受的。

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, m, left, right;
    scanf("%d", &n);
    int dist[n + 1], sum = 0;
    for (int i = 1; i <= n; i++) { //为方便序号从1开始
        scanf("%d", &dist[i]);
        sum += dist[i]; //输入时就得到整个圆环的长度
    }
    scanf("%d", &m);
    while (m--) {
        scanf("%d%d", &left, &right);
        if (left > right) swap(left, right);
        int leftSum = 0, rightSum = 0; //从左到右的距离
        for (int i = left; i <= right - 1; i++) leftSum += dist[i];
        rightSum = sum - leftSum; 
        printf("%d\n", leftSum < rightSum ? leftSum : rightSum);
    }
    return 0;
}

改进一下,避免遍历数组,就应该在输入时进行预处理,令di[n]数组中的di[i]表示1号结点到达i号结点顺时针的下一个结点的距离(1<=i<=N),这样查询left->right,结果就是di(left, right)与sum-di[left, right)的比较:

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, m, left, right;
    scanf("%d", &n);
    int dist[n + 1], di[n + 1] = {0}, sum = 0;
    for (int i = 1; i <= n; i++) { //为方便序号从1开始
        scanf("%d", &dist[i]);
        sum += dist[i]; //输入时就得到整个圆环的长度
        di[i] = sum; //表示1号结点到达i号结点顺时针的下一个结点的距离
    }
    scanf("%d", &m);
    while (m--) {
        scanf("%d%d", &left, &right);
        if (left > right) swap(left, right);
        int leftSum = di[right - 1] - di[left - 1]; //从左到右的距离
        printf("%d\n", min(leftSum, sum - leftSum));
    }
    return 0;
}

可以看到时间消耗少,空间占用则变大。
在这里插入图片描述

A1047 Student List for Course (25 分) 0.26

书上写的时间限制为400ms,内存为64000kb。题目中的是时间限制: 1000 ms,内存限制: 64 MB。内存差不多。时间上面我们可以用一下stl,写得简单一点。用到了vector数组+c_str()输出,如果直接使用vector<set<string>>+cout,最后一组数据就会超时。

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, k, num, id;
    scanf("%d%d", &n, &k);
    vector<string> courStuList[k + 1]; //课程的学生名单
    string t;
    for (int i = 1; i <= n; i++) {
        cin >> t; 
        scanf("%d", &num);
        while (num--) {
            scanf("%d", &id);
            courStuList[id].push_back(t);
        }
    }
    for (int i = 1; i <= k; i++) {
        printf("%d %d\n", i, courStuList[i].size());
        sort(courStuList[i].begin(), courStuList[i].end());
        for (int j = 0; j < courStuList[i].size(); j++)
    		printf("%s\n", courStuList[i][j].c_str());
    }
    return 0;
}

★ A1048 Find Coins (25) 0.30

Eva loves to collect coins from all over the universe, including some other planets like Mars. One day she visited a universal shopping mall which could accept all kinds of coins as payments. However, there was a special requirement of the payment: for each bill, she could only use exactly two coins to pay the exact amount. Since she has as many as 10​5​​ coins with her, she definitely needs your help. You are supposed to tell her, for any given amount of money, whether or not she can find two coins to pay for it.

  • Input Specification:
    Each input file contains one test case. For each case, the first line contains 2 positive numbers: N (≤10​5​​, the total number of coins) and M (≤10​3​​, the amount of money Eva has to pay). The second line contains N face values of the coins, which are all positive numbers no more than 500. All the numbers in a line are separated by a space.
  • Output Specification:
    For each test case, print in one line the two face values V​1​​ and V​2​​ (separated by a space) such that V​1​​+V​2​​=M and V​1​​≤V​2​​. If such a solution is not unique, output the one with the smallest V​1​​. If there is no solution, output No Solution instead.
  • Sample Input 1:
    8 15
    1 2 8 7 2 4 11 15
    
  • Sample Output 1:
    4 11
    
  • Sample Input 2:
    7 14
    1 8 7 2 4 11 15
    
  • Sample Output 2:
    No Solution
    

这道题虽然只是整数哈希,但是有很多陷阱,一不小心就会错几个测试点。测试的时候不仅要通过样例,还要尽可能地多想出几组边界数据。把空间开大一点。

#include <bits/stdc++.h>
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    int hashCoin[1010] = {0}, coin;
    for (int i = 0; i < n; i++) {
        scanf("%d", &coin);
        hashCoin[coin]++; //统计次数, 隐形排序
    }
    for (int i = 1; i < m; i++) {
        if (hashCoin[i]) {
            hashCoin[i]--; //除去一枚, 打算用掉
            if (hashCoin[m - i]) { //存在互补的一枚
                printf("%d %d", i, m - i);
                return 0;
            } 
            hashCoin[i]++; //不存在互补的硬币, 没用掉
        }
    }
    printf("No Solution\n");
    return 0;
}

☆☆ A1049 Counting Ones (30 分) 0.37

The task is simple: given any positive integer N, you are supposed to count the total number of 1’s in the decimal form of the integers from 1 to N. For example, given N being 12, there are five 1’s in 1, 10, 11, and 12.

  • Input Specification:
    Each input file contains one test case which gives the positive N (≤2​30​​).
  • Output Specification:
    For each test case, print the number of 1’s in one line.
  • Sample Input:
    12
    
  • Sample Output:
    5
    
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, a = 1, ans = 0;
    int left, now, right;
    scanf("%d", &n);
    while (n / a != 0) {
        left = n / (a * 10);
        now = n / a % 10;
        right = n % a;
        if (now == 0) ans += left * a;
        else if (now == 1) ans += left * a + right + 1;
        else ans += (left + 1) * a;
        a *= 10;
    }
    printf("%d\n", ans);
    return 0;
}

A1050 String Subtraction (20 分) 0.41

输入的字符包括字母数字和各种符号这些可见ascll字符,还有空格,因此需要能容纳整个ascll码的哈希表。这题虽然是字符串,但实际上还是整数哈希。CodeUP上面做过的题。
我这里使用的读入一行的方法不太一样,而且把换行符吸收掉了。

#include <bits/stdc++.h>

int main() {
    char r[10010], s[10010];
    int hashTable[130] = {0};
    scanf("%[^\n]\n", r); 
    scanf("%[^\n]\n", s);
    for (int i = 0; s[i]; i++) 
        hashTable[s[i]] = 1;
    for (int i = 0; r[i]; i++) 
        if (hashTable[r[i]] == 0) printf("%c", r[i]);
    return 0;
}
已标记关键词 清除标记
课程简介: 历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Boot、Spring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:
©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页