天梯图书阅览室请你编写一个简单的图书借阅统计程序。当读者借书时,管理员输入书号并按下S
键,程序开始计时;当读者还书时,管理员输入书号并按下E
键,程序结束计时。书号为不超过1000的正整数。当管理员将0作为书号输入时,表示一天工作结束,你的程序应输出当天的读者借书次数和平均阅读时间。
注意:由于线路偶尔会有故障,可能出现不完整的纪录,即只有S
没有E
,或者只有E
没有S
的纪录,系统应能自动忽略这种无效纪录。另外,题目保证书号是书的唯一标识,同一本书在任何时间区间内只可能被一位读者借阅。
输入格式:
输入在第一行给出一个正整数N(≤10),随后给出N天的纪录。每天的纪录由若干次借阅操作组成,每次操作占一行,格式为:
书号
([1, 1000]内的整数) 键值
(S
或E
) 发生时间
(hh:mm
,其中hh
是[0,23]内的整数,mm
是[0, 59]内整数)
每一天的纪录保证按时间递增的顺序给出。
输出格式:
对每天的纪录,在一行中输出当天的读者借书次数和平均阅读时间(以分钟为单位的精确到个位的整数时间)。
输入样例:
3
1 S 08:10
2 S 08:35
1 E 10:00
2 E 13:16
0 S 17:00
0 S 17:00
3 E 08:10
1 S 08:20
2 S 09:00
1 E 09:20
0 E 17:00
输出样例:
2 196
0 0
1 60
感觉有可能是一本书会在同一天被借出还回多次,所以当时的代码WA了。
#include"bits/stdc++.h" using namespace std; const int maxn = 1100; struct node { int n; int sh,sm,eh,em; }e[maxn]; int main() { // freopen("input.txt","r",stdin); int n; cin>>n; int cnt = 0; int val; char ch; while(cnt<n) { for(int i=0;i<maxn;i++) e[i].n = 0; while(scanf("%d",&val)!=EOF) { if(val==0) { cnt++; cin>>ch; int ta,tb; scanf("%2d:%2d",&ta,&tb); break; } cin>>ch; if(ch=='S') scanf("%2d:%2d",&e[val].sh,&e[val].sm); else scanf("%2d:%2d",&e[val].eh,&e[val].em); //printf("%2d %2d ",e[val].sh,e[val].sm); e[val].n++; } int sum = 0; //cout<<23232342<<endl; int ans = 0; for(int i=0;i<maxn;i++) { if(e[i].n==2) { ans++; //cout<<e[i].sh<<" "<<e[i].eh<<endl; sum = sum+ (e[i].eh-e[i].sh)*60+e[i].em-e[i].sm; //cout<<sum<<endl; } } cout<<ans; printf(" %.0f ",(ans==0?0:1.0*sum/ans)); } return 0; }
#include "bits/stdc++.h" using namespace std; int e[11000]; int main() { //freopen("input.txt", "r", stdin); int n; scanf("%d", &n); int h, m, val; char ch; for (int i = 0; i < n; i++) { int sum = 0, cnt = 0; memset(e, -1, sizeof(e)); while ((scanf("%d %c %d:%d", &val, &ch, &h, &m) != EOF) && val != 0) { if (ch == 'S') { e[val] = h * 60 + m; } else if (ch == 'E' && e[val] != -1) { //本来这里写的是e[val]>0,wa了两个点。借书有可能是在00:00借的。。。。 cnt++; sum = sum + h * 60 + m - e[val]; e[val] = -1; } } if (cnt == 0) { printf("0 0 "); } else { printf("%d %.0f ", cnt, 1.0 * sum / cnt); } } return 0; }
这里所谓的“光棍”,并不是指单身汪啦~ 说的是全部由1组成的数字,比如1、11、111、1111等。传说任何一个光棍都能被一个不以5结尾的奇数整除。比如,111111就可以被13整除。 现在,你的程序要读入一个整数x
,这个整数一定是奇数并且不以5结尾。然后,经过计算,输出两个数字:第一个数字s
,表示x
乘以s
是一个光棍,第二个数字n
是这个光棍的位数。这样的解当然不是唯一的,题目要求你输出最小的解。
提示:一个显然的办法是逐渐增加光棍的位数,直到可以整除x
为止。但难点在于,s
可能是个非常大的数 —— 比如,程序输入31,那么就输出3584229390681和15,因为31乘以3584229390681的结果是111111111111111,一共15个1。
输入格式:
输入在一行中给出一个不以5结尾的正奇数x
(<1000)。
输出格式:
在一行中输出相应的最小的s
和n
,其间以1个空格分隔。
输入样例:
31
输出样例:
3584229390681 15
import java.math.BigInteger; import java.util.BitSet; import java.util.Scanner; public class Main { public static void main(String args[]) { Scanner in = new Scanner(System.in); BigInteger l, a; l = BigInteger.valueOf(1); a = in.nextBigInteger(); int cnt = 1; //System.out.println(l.mod(a)); while (l.mod(a)!=BigInteger.valueOf(0)) { l = l.multiply(BigInteger.valueOf(10)).add(BigInteger.valueOf(1)); cnt++; //System.out.println(l + " " + cnt); } System.out.println(l.divide(a) + " " + cnt); } }
#include "bits/stdc++.h" using namespace std; int e[11000]; int main() { //freopen("input.txt", "r", stdin) int cnt = 1; int l = 1; int a; cin >> a; int flag = 0; while (l != 0) { if (l / a) flag = 1; if (flag) cout << l / a; l %= a; if (l == 0) break; l = l * 10 + 1; cnt++; } cout << " " << cnt << endl; return 0; }
给定一个单链表 L1→L2→⋯→Ln−1→Ln,请编写程序将链表重新排列为 Ln→L1→Ln−1→L2→⋯。例如:给定L为1→2→3→4→5→6,则输出应该为6→1→5→2→4→3。
输入格式:
每个输入包含1个测试用例。每个测试用例第1行给出第1个结点的地址和结点总个数,即正整数N (≤105)。结点的地址是5位非负整数,NULL地址用−1表示。
接下来有N行,每行格式为:
Address Data Next
其中Address
是结点地址;Data
是该结点保存的数据,为不超过105的正整数;Next
是下一结点的地址。题目保证给出的链表上至少有两个结点。
输出格式:
对每个测试用例,顺序输出重排后的结果链表,其上每个结点占一行,格式与输入相同。
输入样例:
00100 6
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
输出样例:
68237 6 00100
00100 1 99999
99999 5 12309
12309 2 00000
00000 4 33218
33218 3 -1
利用数组记录链表节点顺序
#include"bits/stdc++.h" using namespace std; const int maxn = 1e6; int a[maxn], b[maxn], Next[maxn]; int c[maxn]; int main() { //freopen("input.txt", "r", stdin); int head, n; cin >> head >> n; int x, y, z; for (int i = 0; i < n; i++) { cin >> x >> y >> z; a[x] = y; Next[x] = z; } int t = head; int tot = 0; while (t != -1) { b[tot++] = t; t = Next[t]; } int p = 0; int cnt = 0; for (int i = 0; i < tot; i++) { //cout << a[b[i]] << endl; c[cnt++] = b[tot - i - 1]; c[cnt++] = b[i]; } printf("%05d %d", c[0], a[c[0]]); for (int i = 1; i < tot; i++) { printf(" %05d %05d %d", c[i], c[i], a[c[i]]); } printf(" -1 "); return 0; }
二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉搜索树。(摘自百度百科)
给定一系列互不相等的整数,将它们顺次插入一棵初始为空的二叉搜索树,然后对结果树的结构进行描述。你需要能判断给定的描述是否正确。例如将{ 2 4 1 3 0 }插入后,得到一棵二叉搜索树,则陈述句如“2是树的根”、“1和4是兄弟结点”、“3和0在同一层上”(指自顶向下的深度相同)、“2是4的双亲结点”、“3是4的左孩子”都是正确的;而“4是2的左孩子”、“1和3是兄弟结点”都是不正确的。
输入格式:
输入在第一行给出一个正整数N(≤100),随后一行给出N个互不相同的整数,数字间以空格分隔,要求将之顺次插入一棵初始为空的二叉搜索树。之后给出一个正整数M(≤100),随后M行,每行给出一句待判断的陈述句。陈述句有以下6种:
A is the root
,即"A
是树的根";A and B are siblings
,即"A
和B
是兄弟结点";A is the parent of B
,即"A
是B
的双亲结点";A is the left child of B
,即"A
是B
的左孩子";A is the right child of B
,即"A
是B
的右孩子";A and B are on the same level
,即"A
和B
在同一层上"。
题目保证所有给定的整数都在整型范围内。
输出格式:
对每句陈述,如果正确则输出Yes
,否则输出No
,每句占一行。
输入样例:
5
2 4 1 3 0
8
2 is the root
1 and 4 are siblings
3 and 0 are on the same level
2 is the parent of 4
3 is the left child of 4
1 is the right child of 2
4 and 0 are on the same level
100 is the right child of 3
输出样例:
Yes
Yes
Yes
Yes
Yes
No
No
No
用vis记录元素在树中的位置会段错误。。。
#include "bits/stdc++.h" using namespace std; const int maxn = 1e5; int tree[maxn]; int flag; void build(int val, int pos) { if (tree[pos] == -1) { tree[pos] = val; return; } if (val < tree[pos]) build(val, pos * 2); else build(val, pos * 2 + 1); } int dee(int n) { int ret = 0; while (n > 1) { ret++; n /= 2; } return ret; } int x, y; int xid, yid; void find() { xid = -1; yid = -1; for (int i = 1; i < maxn; i++) { if (tree[i] == x) xid = i; if (tree[i] == y) yid = i; if (xid != -1 && yid != -1) break; } } int main() { freopen("input.txt", "r", stdin); memset(tree, -1, sizeof(tree)); int n, a; cin >> n; for (int i = 0; i < n; i++) { cin >> a; build(a, 1); } int k; cin >> k; string str; while (k--) { flag = 0; cin >> x >> str; if (str[0] == 'i') { cin >> str; cin >> str; if (str[0] == 'r' && str[1] == 'o')//root { if (tree[1] == x) flag = 1; } else if (str[0] == 'p') {//parent cin >> str >> y; find(); if (yid / 2 == xid) flag = 1; } else if (str[0] == 'l') {//lson cin >> str; cin >> str >> y; find(); if (yid * 2 == xid) flag = 1; } else { cin >> str; cin >> str >> y; find(); if (yid * 2 + 1 == xid) flag = 1; } } else { cin >> y >> str; cin >> str; if (str[0] == 's') {//siblings find(); if (yid / 2 == xid / 2) flag = 1; } else { cin >> str; cin >> str; cin >> str; find(); if (dee(xid) == dee(yid)) flag = 1; } } if (xid == -1 || yid == -1) flag = 0; if (flag) printf("Yes "); else printf("No "); } return 0; }
L3-017 森森快递 (30 分)
森森开了一家快递公司,叫森森快递。因为公司刚刚开张,所以业务路线很简单,可以认为是一条直线上的N个城市,这些城市从左到右依次从0到(编号。由于道路限制,第i号城市(,)与第(号城市中间往返的运输货物重量在同一时刻不能超过Ci公斤。
公司开张后很快接到了Q张订单,其中j张订单描述了某些指定的货物要从Sj号城市运输到Tj号城市。这里我们简单地假设所有货物都有无限货源,森森会不定时地挑选其中一部分货物进行运输。安全起见,这些货物不会在中途卸货。
为了让公司整体效益更佳,森森想知道如何安排订单的运输,能使得运输的货物重量最大且符合道路的限制?要注意的是,发货时间有可能是任何时刻,所以我们安排订单的时候,必须保证共用同一条道路的所有货车的总重量不超载。例如我们安排1号城市到4号城市以及2号城市到4号城市两张订单的运输,则这两张订单的运输同时受2-3以及3-4两条道路的限制,因为两张订单的货物可能会同时在这些道路上运输。
输入格式:
输入在第一行给出两个正整数N和Q(2, 1),表示总共的城市数以及订单数量。
第二行给出(个数,顺次表示相邻两城市间的道路允许的最大运货重量Ci(,)。题目保证每个Ci是不超过231的非负整数。
接下来Q行,每行给出一张订单的起始及终止运输城市编号。题目保证所有编号合法,并且不存在起点和终点重合的情况。
输出格式:
在一行中输出可运输货物的最大重量。
输入样例:
10 6
0 7 8 5 2 3 1 9 10
0 9
1 8
2 7
6 3
4 5
4 2
输出样例:
7
样例提示:我们选择执行最后两张订单,即把5公斤货从城市4运到城市2,并且把2公斤货从城市4运到城市5,就可以得到最大运输量7公斤。
显然在覆盖区间相同时,所用的段数越多越优,贪心+线段树。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 100; struct node { int l, r; } e[maxn]; struct tre { int l, r; ll add, minn; } tree[maxn * 4]; bool cmp(node a, node b) { if (a.r != b.r) return a.r < b.r; return a.l < b.l; } void build(int p, int l, int r) { tree[p].l = l; tree[p].r = r; if (l == r) { //tree[p].val = a[l]; scanf("%lld", &tree[p].minn); return; } int mid = (l + r) / 2; build(p * 2, l, mid); build(p * 2 + 1, mid + 1, r); tree[p].minn = min(tree[p * 2].minn, tree[p * 2 + 1].minn); } void spread(int p) { if (tree[p].add) { tree[p * 2].minn += tree[p].add; tree[p * 2 + 1].minn += tree[p].add; tree[p * 2].add += tree[p].add; tree[p * 2 + 1].add += tree[p].add; tree[p].add = 0; } } void change(int p, int l, int r, ll d) { if (l <= tree[p].l && r >= tree[p].r) { tree[p].minn += d; tree[p].add += d; return; } spread(p); int mid = (tree[p].l + tree[p].r) / 2; if (l <= mid) change(p * 2, l, r, d); if (r > mid) change(p * 2 + 1, l, r, d); tree[p].minn = min(tree[p * 2].minn, tree[p * 2 + 1].minn); } ll ask(int p, int l, int r) { if (l <= tree[p].l && r >= tree[p].r) { return tree[p].minn; } spread(p); int mid = (tree[p].l + tree[p].r) / 2; ll val = 1ll << 35; if (l <= mid) val = min(val, ask(p * 2, l, r)); if (r > mid) val = min(val, ask(p * 2 + 1, l, r)); return val; } int main() { // freopen("input.txt", "r", stdin); int n, q; scanf("%d %d", &n, &q); build(1, 1, n - 1); for (int i = 0; i < q; i++) { scanf("%d %d", &e[i].l, &e[i].r); if (e[i].r < e[i].l) swap(e[i].l, e[i].r); } sort(e, e + q, cmp); ll ans = 0; ll v = 0; for (int i = 0; i < q; i++) { v = ask(1, e[i].l + 1, e[i].r); if (v > 0) { ans += v; change(1, e[i].l + 1, e[i].r, -v); } } printf("%lld ", ans); return 0; }
L3-018 森森美图 (30 分)
森森最近想让自己的朋友圈熠熠生辉,所以他决定自己写个美化照片的软件,并起名为森森美图。众所周知,在合照中美化自己的面部而不美化合照者的面部是让自己占据朋友圈高点的绝好方法,因此森森美图里当然得有这个功能。 这个功能的第一步是将自己的面部选中。森森首先计算出了一个图像中所有像素点与周围点的相似程度的分数,分数越低表示某个像素点越“像”一个轮廓边缘上的点。 森森认为,任意连续像素点的得分之和越低,表示它们组成的曲线和轮廓边缘的重合程度越高。为了选择出一个完整的面部,森森决定让用户选择面部上的两个像素点A和B,则连接这两个点的直线就将图像分为两部分,然后在这两部分中分别寻找一条从A到B且与轮廓重合程度最高的曲线,就可以拼出用户的面部了。 然而森森计算出来得分矩阵后,突然发现自己不知道怎么找到这两条曲线了,你能帮森森当上朋友圈的小王子吗?
为了解题方便,我们做出以下补充说明:
- 图像的左上角是坐标原点(0,0),我们假设所有像素按矩阵格式排列,其坐标均为非负整数(即横轴向右为正,纵轴向下为正)。
- 忽略正好位于连接A和B的直线(注意不是线段)上的像素点,即不认为这部分像素点在任何一个划分部分上,因此曲线也不能经过这部分像素点。
- 曲线是八连通的(即任一像素点可与其周围的8个像素连通),但为了计算准确,某像素连接对角相邻的斜向像素时,得分额外增加两个像素分数和的√2倍减一。例如样例中,经过坐标为(3,1)和(4,2)的两个像素点的曲线,其得分应该是这两个像素点的分数和(2+2),再加上额外的(2+2)乘以(√2−1),即约为5.66。
输入格式:
输入在第一行给出两个正整数N和M(5),表示像素得分矩阵的行数和列数。
接下来N行,每行M个不大于1000的非负整数,即为像素点的分值。
最后一行给出用户选择的起始和结束像素点的坐标(和(。4个整数用空格分隔。
输出格式:
在一行中输出划分图片后找到的轮廓曲线的得分和,保留小数点后两位。注意起点和终点的得分不要重复计算。
输入样例:
6 6
9 0 1 9 9 9
9 9 1 2 2 9
9 9 2 0 2 9
9 9 1 1 2 9
9 9 3 3 1 1
9 9 9 9 9 9
2 1 5 4
输出样例:
27.04
利用叉乘判断点的位置,然后每次找出半圈的最短路。训练的时候不知道怎么想的就用dfs来找最短路径。。。。
#include "bits/stdc++.h" using namespace std; const int inf = 0x3f3f3f3f; int n, m; double mp[200][200]; int vis[200][200]; double dist[200][200]; int dx[] = {-1, 0, 0, 1, -1, -1, 1, 1}; int dy[] = {0, -1, 1, 0, -1, 1, -1, 1}; bool up(int sx, int sy, int ex, int ey, int x, int y) { return (x - sx) * (ey - y) < (y - sy) * (ex - x); } double spfa(int sx, int sy, int ex, int ey) { for (int i = 0; i < 110; i++) { for (int j = 0; j < 110; j++) { vis[i][j] = 0; dist[i][j] = inf; } } queue<int> qx; queue<int> qy; qx.push(sx); qy.push(sy); dist[sx][sy] = mp[sx][sy]; vis[sx][sy] = 1; int x, y, tx, ty; while (!qx.empty()) { x = qx.front(), qx.pop(); y = qy.front(), qy.pop(); //cout << x << " " << y << endl; vis[x][y] = 0; double w; for (int i = 0; i < 8; i++) { tx = x + dx[i]; ty = y + dy[i]; if (tx < 0 || tx >= n || ty < 0 || ty >= m) continue; if (up(sx, sy, ex, ey, tx, ty) || (tx == ex && ty == ey)) { w = mp[tx][ty]; if (i > 3) w = w + (mp[x][y] + mp[tx][ty]) * (sqrt(2.0) - 1); if (dist[tx][ty] > dist[x][y] + w) { dist[tx][ty] = dist[x][y] + w; if (!vis[tx][ty]) { qx.push(tx); qy.push(ty); vis[tx][ty] = 1; } } } } } return dist[ex][ey]; } int main() { //freopen("input.txt", "r", stdin); int sx, sy, ex, ey; cin >> n >> m; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> mp[i][j]; } } cin >> sy >> sx >> ey >> ex; double out = 0; out += spfa(sx, sy, ex, ey); //printf("%.2f ", out); out += spfa(ex, ey, sx, sy); //printf("%.2f ", out); out = out - mp[sx][sy] - mp[ex][ey]; printf("%.2f ", out); return 0; }