《PTA练习*补》
列车调度 (25分):
这题被之前做过的一个列车的题误导了,一直以为是栈的运用,就一直在思考栈的方向,最后挂了..
思路:
因为是递减出去,所以肯定是大的列车先出去,所以,对于每个车,要开辟新的位置来存放它,就是后面有比他大的。
因为如果比他大的插入在某个位置后面,那个大的值显然不是队首,不能先出去。
那么,这题就是个nlogn求最长上升子序列的问题。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<string,int> pii; const int N = 5e5+5; const int M = 2e5+5; const LL Mod = 1e9+7; #define rg register #define pi acos(-1) #define INF 1e9 #define CT0 cin.tie(0),cout.tie(0) #define IO ios::sync_with_stdio(false) #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline LL read(){ LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } void print(int x){ if(x < 0){x = -x;putchar('-');} if(x > 9) print(x/10); putchar(x%10+'0'); } } using namespace FASTIO; void FRE(){/*freopen("data1.in","r",stdin); freopen("data1.out","w",stdout);*/} int dp[N],a[N]; int main() { int n;n = read(); for(int i = 1;i <= n;++i) a[i] = read(); int len = 0; dp[++len] = a[1]; for(int i = 2;i <= n;++i) { if(dp[len] < a[i]) dp[++len] = a[i]; else { int pos = upper_bound(dp+1,dp+len+1,a[i])-dp; dp[pos] = a[i]; } } printf("%d\n",len); system("pause"); }
二叉搜索树的结构 (30分)
这题一开始二叉树的写法觉得指针很麻烦,就用了动态开点的线段树来写了。
插入之后计算下fa,dep数组就很简单了。
这里有两个坑点没注意少了6分。
1:节点数据可能很大,要离散化(其实这里想到了,只是没时间去写了。)
2:因为节点数据随机,不存在用0会被卡,要用更特殊的(这里用了-13)。然后可能查询的节点可能不存在,这里也要特判
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e6+5; const int M = 2e5+5; const LL Mod = 1e9+7; #define rg register #define pi acos(-1) #define INF 1e8 #define CT0 cin.tie(0),cout.tie(0) #define IO ios::sync_with_stdio(false) #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline LL read(){ LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } void print(int x){ if(x < 0){x = -x;putchar('-');} if(x > 9) print(x/10); putchar(x%10+'0'); } } using namespace FASTIO; struct Node{ int val,L,r; }node[N*20]; map<int,int> mp; int rt = -13,fa[105],dep[105],L[105],R[105],cnt = 0,tim = 0; void Insert(int &idx,int x) { if(idx == -13) { idx = ++cnt; if(rt == -13) rt = cnt; node[idx].val = x; node[idx].L = node[idx].r = -13; return ; } if(x < node[idx].val) Insert(node[idx].L,x); else Insert(node[idx].r,x); } void dfs(int idx,int ffa,int tag) { if(idx == -13) return ; int u = node[idx].val; if(ffa != -13) { dep[mp[u]] = dep[mp[ffa]]+1; fa[mp[u]] = ffa;//存具体的值 if(tag == 0) L[mp[ffa]] = u; else R[mp[ffa]] = u; } dfs(node[idx].L,u,0); dfs(node[idx].r,u,1); } int main() { int n;n = read(); while(n--) { int x;x = read(); mp[x] = ++tim; Insert(rt,x); } dfs(rt,-13,0); int m;m = read(); while(m--) { int x,y;x = read(); string s;cin >> s; if(s == "is") { string the,a,b;cin >> the >> a; if(a == "root") printf("%s\n",node[rt].val == x && mp[x] != 0 ? "Yes" : "No"); else if(a == "parent") { cin >> b >> y; printf("%s\n",fa[mp[y]] == x && mp[y] != 0 && mp[x] != 0 ? "Yes" : "No"); } else if(a == "left") { string c,d;cin >> c >> d >> y; printf("%s\n",L[mp[y]] == x && mp[y] != 0 && mp[x] != 0 ? "Yes" : "No"); } else { string c,d;cin >> c >> d >> y; printf("%s\n",R[mp[y]] == x && mp[x] != 0 && mp[y] != 0 ? "Yes" : "No"); } } else { cin >> y; string a,b;cin >> a >> b; if(b == "siblings") printf("%s\n",fa[mp[x]] == fa[mp[y]] && mp[x] != 0 && mp[y] != 0 ? "Yes" : "No"); else { string c,d,e;cin >> c >> d >> e; printf("%s\n",dep[mp[x]] == dep[mp[y]] && mp[x] != 0 && mp[y] != 0 ? "Yes" : "No"); } } } system("pause"); }
L3-001 凑零钱 (30分)
01背包路径打印。
解法1:dfs暴搜。
剪枝够强就能过。
这里可以的话,如果后面的数很大,那么很容易就超m,剪枝掉。
然后很小的数的话,就可能有很层了,那么这里就需要一个很重要的剪枝,记录一下后缀和,当剩下的全部都无法凑成,就return。
解法2:dp。
看到网上都是把价值当作容量来dp的。
其实根本不需要,因为这题要打印的是最小序列,所以我们可以对数组降序,后面如果到了i,i可以和前面的凑成m,显然i这组字典序更小。
因为我们降序排后,i肯定比前面的都小,那么加入i,显然可以使字典序最小。
然后记录下就行,最后我们从后往前判断输出
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e4+5; const int M = 2e5+5; const LL Mod = 1e9+7; #define rg register #define pi acos(-1) #define INF 1e8 #define CT0 cin.tie(0),cout.tie(0) #define IO ios::sync_with_stdio(false) #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline LL read(){ LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } void print(int x){ if(x < 0){x = -x;putchar('-');} if(x > 9) print(x/10); putchar(x%10+'0'); } } using namespace FASTIO; int a[N]; int dp[105],pre[N][105]; int main() { int n,m;n = read(),m = read(); for(int i = 1;i <= n;++i) a[i] = read(); sort(a+1,a+n+1,greater<int>()); dp[0] = 1; for(int i = 1;i <= n;++i) { for(int j = m;j >= a[i];--j) { if(dp[j-a[i]] == 0) continue; dp[j] = 1; pre[i][j] = 1; } } if(!dp[m]) printf("No Solution\n"); else { int f = 1; for(int i = n;i >= 1;--i) { if(pre[i][m]) { if(f) printf("%d",a[i]); else printf(" %d",a[i]); m -= a[i]; f = 0; } } } system("pause"); }
L1-006 连续因子 (20分)
这题一开始题意没读懂..
其实就是求某段连续因子,并且这段因子不需要满足相乘 = n,只要能整除就行。
那么,显然筛到sqrt(n)就够,因为sqrt(n) * sqrt(n)+1肯定 > n,然后枚举连续的开头,再去找就行。
这里有一个坑点,就是一个因子也比他自己本身因子要小。
所以满足自己是因子的只有素数。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL,int> pii; const int N = 360; const int M = 2e5+5; const LL Mod = 1e9+7; #define rg register #define pi acos(-1) #define INF 1e18 #define CT0 cin.tie(0),cout.tie(0) #define IO ios::sync_with_stdio(false) #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline LL read(){ LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } void print(int x){ if(x < 0){x = -x;putchar('-');} if(x > 9) print(x/10); putchar(x%10+'0'); } } using namespace FASTIO; int main() { int n;n = read(); int len = 0,st = 0,m = sqrt(n); for(rg int i = 2;i <= m;++i) { if(n % i == 0) { int ma = n,j = i,tmp = 0; while(ma % j == 0) { tmp++; ma /= j; j++; } if(tmp > len) { len = tmp,st = i; } } } if(len == 0) printf("1\n%d\n",n); else { printf("%d\n",len); for(int i = st;i <= st+len-1;++i) printf("%d%c",i,i == st+len-1 ? '\n' : '*'); } system("pause"); }
7-14 二叉搜索树的最近公共祖先 (30分)
终于a了。
一开始TLE应该是因为insert中map调用太多次了。
实际上这里是可以离散化数据的(一开始以为不能离散化)
离散化之后要注意根的点和值。然后常数优化倍增就行了。
复杂度肯定是够的,就是细节调了很久。。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e4+5; const int M = 2e5+5; const LL Mod = 1e9+7; #define rg register #define pi acos(-1) #define INF 1e9 #define CT0 cin.tie(0),cout.tie(0) #define IO ios::sync_with_stdio(false) #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline LL read(){ LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } void print(int x){ if(x < 0){x = -x;putchar('-');} if(x > 9) print(x/10); putchar(x%10+'0'); } } using namespace FASTIO; int m,n,tot = 0,cnt = 0,rt = 0; int fa[N],L[N],r[N],val[N],dep[N],f[N][25],lg[N],a[N],b[N]; vector<int> G[N]; inline void init() { for(rg int i = 1;i < N;++i) lg[i] = lg[i-1] + ((1<<lg[i-1]) == i); } void Insert(int now,int ffa,int x,int id) { if(now == 0) { if(id == 1) L[ffa] = x; else r[ffa] = x; fa[x] = ffa; } else { if(val[x] > val[now]) Insert(r[now],now,x,0); else Insert(L[now],now,x,1); } } void dfs(int u,int fa) { dep[u] = dep[fa]+1;f[u][0] = fa; for(rg int i = 1;i <= lg[dep[u]];++i) f[u][i] = f[f[u][i-1]][i-1]; for(auto v : G[u]) if(v != fa) dfs(v,u); } int LCA(int x,int y) { if(dep[x] < dep[y]) swap(x,y); while(dep[x] > dep[y]) x = f[x][lg[dep[x]-dep[y]]-1]; if(x == y) return x; for(rg int i = lg[dep[x]]-1;i >= 0;--i) if(f[x][i] != f[y][i]) x = f[x][i],y = f[y][i]; return f[x][0]; } int main() { init(); m = read(),n = read(); for(rg int i = 1;i <= n;++i) a[i] = read(),b[i] = a[i]; sort(a+1,a+n+1); for(rg int i = 1;i <= n;++i) { int ma = b[i]; b[i] = lower_bound(a+1,a+n+1,b[i])-a; val[b[i]] = ma; L[b[i]] = r[b[i]] = 0; if(rt == 0) rt = b[i]; else Insert(rt,0,b[i],0); } for(rg int i = 1;i <= n;++i) if(fa[i] != 0) G[i].push_back(fa[i]),G[fa[i]].push_back(i); dfs(rt,0); while(m--) { int u,v;u = read(),v = read(); int pos1 = lower_bound(a+1,a+n+1,u)-a; int pos2 = lower_bound(a+1,a+n+1,v)-a; int f1 = 0,f2 = 0; if(pos1 > n || a[pos1] != u) f1 = 1; if(pos2 > n || a[pos2] != v) f2 = 1; if(f1 && f2) printf("ERROR: %d and %d are not found.\n",u,v); else if(f1) printf("ERROR: %d is not found.\n",u); else if(f2) printf("ERROR: %d is not found.\n",v); else { int lca = LCA(pos1,pos2); if(val[lca] == u) printf("%d is an ancestor of %d.\n",u,v); else if(val[lca] == v) printf("%d is an ancestor of %d.\n",v,u); else printf("LCA of %d and %d is %d.\n",u,v,val[lca]); } } system("pause"); }
7-84 关键活动 (30分)
这个题关于了一个AOE网中的概念。
补了补。
对于关键路径就是无环的有向带权图中的最长完工路径。
同时这条路径也是保证工程完成的最短时间。
而关键活动就是这条路径上的路径。
对于关键路径的求解:需要两次拓扑排序,去更新最早开始时间,和最早完工时间。
首先判环。没环才存在。
对于最早开始时间,因为需要前面的依赖工作都完成,才去干下面的,所以是去max值。
对于最早完工时间,从建反图,从终点开始拓扑,取min去位置。
最后对于一条路径,如果它的权值 = 它的结束节点的最早完工时间 - 它的开始节点的最早开始时间,那么就是关键活动。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e5+5; const int M = 1e6+5; const LL Mod = 1e9+7; #define pi acos(-1) #define INF 1e18 #define CT0 cin.tie(0),cout.tie(0) #define IO ios::sync_with_stdio(false) #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline LL read(){ LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } } using namespace FASTIO; int in[105],on[105],n,m,st[105],ed[105]; vector<pii> G[105],RG[105]; struct Node{int x,y;}; bool cmp(Node a,Node b) { if(a.x == b.x) return a.y > b.y; else return a.x < b.x; } int tim; bool solve() { memset(st,0,sizeof(st)); memset(ed,0x3f,sizeof(ed)); queue<int> Q; for(int i = 1;i <= n;++i) if(in[i] == 0) Q.push(i); int ans = 0; while(!Q.empty()) { int u = Q.front(); Q.pop(); ans++; for(auto v : G[u]) { st[v.first] = max(st[v.first],st[u] + v.second); in[v.first]--; if(in[v.first] == 0) Q.push(v.first); } } if(ans < n) return false; int mxtim = 0,pos = 0; for(int i = 1;i <= n;++i) if(st[i] > mxtim) mxtim = st[i],pos = i; ed[pos] = mxtim; while(!Q.empty()) Q.pop(); for(int i = 1;i <= n;++i) if(on[i] == 0) Q.push(i); while(!Q.empty()) { int u = Q.front(); Q.pop(); for(auto v : RG[u]) { ed[v.first] = min(ed[v.first],ed[u] - v.second); on[v.first]--; if(on[v.first] == 0) Q.push(v.first); } } tim = mxtim; return true; } int main() { n = read(),m = read(); while(m--) { int x,y,tim;x = read(),y = read(),tim = read(); G[x].push_back(pii{y,tim}); RG[y].push_back(pii{x,tim}); in[y]++,on[x]++; } if(!solve()) printf("0\n"); else { //for(int i = 1;i <= n;++i) printf("st[%d] is %d ed[%d] is %d\n",i,st[i],i,ed[i]); printf("%d\n",tim); vector<Node> ans; for(int i = 1;i <= n;++i) { for(auto v : G[i]) { if(ed[v.first] - st[i] == v.second) ans.push_back(Node{i,v.first}); } } sort(ans.begin(),ans.end(),cmp); for(auto v : ans) printf("%d->%d\n",v.x,v.y); } // system("pause"); return 0; }