【JLU資料結構榮譽課】第七次上機實驗
->點我進原題
-> 7-1 序列排程
-> 7-2 最大最小差
-> 7-3 二叉樹最短路徑長度
-> 7-4 方案計數
7-1 序列排程 (100 分)
程式碼長度限制 \(16 KB\)
時間限制 \(100 ms\)
記憶體限制 \(10 MB\)
Description
有一個 \(N\) 個數的序列 \(A\):\(1\),\(2\),……,\(N\)。有一個後進先出容器 \(D\),容器的容量為 \(C\)。如果給出一個由 \(1\) 到 \(N\) 組成的序列,那麼可否由 \(A\) 使用容器 \(D\) 的插入和刪除操作得到。
Input
第 \(1\) 行,2個整數 \(T\) 和 \(C\),空格分隔,分別表示詢問的組數和容器的容量,\(1≤T≤10\),\(1≤C≤N\)。
第 \(2\) 到 \(T+1\) 行,每行的第1個整數 \(N\),表示序列的元素數,\(1≤N≤10000\)。接下來 \(N\) 個整數,表示詢問的序列。
Output
\(T\) 行。若第 \(i\) 組的序列能得到,第 \(i\) 行輸出 \(Yes\);否則,第 \(i\) 行輸出 \(No\), \(1≤i≤T\)。
Sample Input
2 2
5 1 2 5 4 3
4 1 3 2 4
Sample Output
No
Yes
思路
不需要真正去建棧,只需要去模擬,找規律能發現符合棧的序列只可能是類似於
x, x+1, x+2, ... x+n, y, y-1, y-2, ... x+n+1, y+1, y+2, ...
的形式,所以只要輸入,然後 \(O(n)\) 對序列進行判斷即可,對於題中的容量 \(C\),保證每一段降序序列不超過 \(C\) 個即可。
程式碼
#include<cstdio> #include<cctype> #include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<map> #define rg register #define ll long long using namespace std; inline int read(){ rg int f = 0, x = 0; rg char ch = getchar(); while(!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while( isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar(); return f? -x: x; } int stk[10001], top; int n, now, book, maxn; signed main(){ int t = read(), c = read(); while(t --){ n = read(); now = 1; maxn = 0; book = 1; for(rg int i = 1; i <= n; ++i){ int tmp = read(); if(tmp == now){ now ++; } else if(tmp < now){ if(tmp == maxn - 1){ maxn = tmp; } else{ book = 0; } } else{ if(tmp - now >= c){ book = 0; } maxn = tmp; now = tmp + 1; } } if(book) printf("Yes\n"); else printf("No\n"); } return 0; }
7-2 最大最小差 (100 分)
程式碼長度限制 \(16 KB\)
時間限制 \(100 ms\)
記憶體限制 \(64 MB\)
Description
對 \(n\) 個正整數,進行如下操作:每一次刪去其中兩個數 \(a\) 和 \(b\),然後加入一個新數:\(a*b+1\),如此下去直到 只剩下一個數。所有按這種操作方式最後得到的數中,最大的為 \(max\),最小的為 \(min\),計算 \(max-min\)。
Input
第1行:\(n\),數列元素的個數,\(1<=n<=16\)。
第2行:\(n\) 個用空格隔開的數 \(x\),\(x<=10\)。
Output
1行,所求 \(max-min\)。
Sample Input
3
2 4 3
Sample Output
2
思路
優先佇列裸題。
程式碼
#include<cstdio>
#include<cctype>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#define rg register
#define ll long long
using namespace std;
inline ll read(){
rg ll f = 0, x = 0;
rg char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while( isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
return f? -x: x;
}
priority_queue<ll> qmin;
priority_queue<ll, vector<ll> , greater<ll> > qmax;
signed main(){
int n;
cin >> n;
for(rg int i = 1; i <= n; ++i){
ll tmp = read();
qmax.push(tmp);
qmin.push(tmp);
}
for(rg int i = 1; i < n; ++i){
ll u = qmax.top();
qmax.pop();
ll v = qmax.top();
qmax.pop();
qmax.push(u * v + 1);
u = qmin.top();
qmin.pop();
v = qmin.top();
qmin.pop();
qmin.push(u * v + 1);
}
printf("%lld", qmax.top() - qmin.top());
return 0;
}
7-3 二叉樹最短路徑長度 (100 分)
程式碼長度限制 \(16 KB\)
時間限制 \(1000 ms\)
記憶體限制 \(10 MB\)
Description
給定一棵二叉樹 \(T\),每個結點賦一個權值。計算從根結點到所有結點的最短路徑長度。路徑長度定義為:路徑上的每個頂點的權值和。
Input
第1行,\(1\) 個整數 \(n\),表示二叉樹 \(T\) 的結點數,結點編號 \(1..n\),\(1≤n≤20000\)。
第2行,\(n\) 個整數,空格分隔,表示 \(T\) 的先根序列,序列中結點用編號表示。
第3行,\(n\) 個整數,空格分隔,表示 \(T\) 的中根序列,序列中結點用編號表示。
第4行,\(n\) 個整數 \(Wi\),空格分隔,表示 \(T\) 中結點的權值,\(-10000≤Wi≤10000\),\(1≤i≤n\)。
Output
1行,n個整數,表示根結點到其它所有結點的最短路徑長度。
Sample Input
4
1 2 4 3
4 2 1 3
1 -1 2 3
Sample Output
1 0 3 3
思路
建樹跑spfa即可,這裡建圖用了一個很巧妙的方法,詳情見程式碼。
程式碼
#include<iostream>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cmath>
#include<queue>
#define N 500010
#define rg register
using namespace std;
const int inf = 0x7fffffff;
struct edge{
int nxt, to, w;
}e[100001];
int tot = 0, head[100001], n;
int fo[20001], mo[20001];
int vis[20001] = {0}, dis[20001], val[20001];
queue <int> q;
inline int read(){
int x=0,f=0;
char ch=getchar();
while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
inline void init(int s){
for(int i=1;i<=n;i++){
dis[i]=inf;
vis[i]=false;
}
dis[s]=0;
q.push(s);
}
inline void add(int u,int v,int w){
e[++tot].to=v;
e[tot].nxt=head[u];
e[tot].w=w;
head[u]=tot;
}
inline void spfa(int s){
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=false;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
if(!vis[v]){
vis[v]=true;
q.push(v);
}
}
}
}
}
inline int build(int lp, int rp, int li, int ri){
if(lp > rp || li > ri){
return -1;
}
for(rg int i = li; i <= ri; ++i){
if(fo[lp] == mo[i]){
int ls = build(lp + 1, i - li + lp, li, i - 1);
int rs = build(i - li + lp + 1, rp, i + 1, ri);
if(ls != -1) add(fo[lp], ls, val[ls]);
if(rs != -1) add(fo[lp], rs, val[rs]);
}
}
return fo[lp];
}
int main(){
n=read();
for(rg int i = 1; i <= n; ++i) fo[i] = read();
for(rg int i = 1; i <= n; ++i) mo[i] = read();
for(rg int i = 1; i <= n; ++i) val[i] = read();
build(1, n, 1, n);
init(1);
spfa(1);
for(int i=1;i<=n;i++){
printf("%d",dis[i]+val[1]);
if(i != n) printf(" ");
}
return 0;
}
7-4 方案計數 (100 分)
程式碼長度限制 \(16 KB\)
時間限制 \(200 ms\)
記憶體限制 \(64 MB\)
Description
組裝一個產品需要 \(n\) 個零件。生產每個零件都需花費一定的時間。零件的生產可以並行進行。有些零件的生產有先後關係,只有一個零件的之前的所有零件都生產完畢,才能開始生產這個零件。如何合理安排工序,才能在最少的時間內完成所有零件的生產。在保證最少時間情況下,關鍵方案有多少種,關鍵方案是指從生產開始時間到結束時間的一個零件生產序列,序列中相鄰兩個零件的關係屬於事先給出的零件間先後關係的集合,序列中的每一個零件的生產都不能延期。
Input
第 \(1\) 行,\(2\) 個整數 \(n\) 和 \(m\),用空格分隔,分別表示零件數和關係數,零件編號 \(1\)..\(n\),\(1≤n≤10000\), \(0≤m≤100000\) 。
第 \(2\) 行,\(n\) 個整數 \(Ti\),用空格分隔,表示零件 \(i\) 的生產時間,\(1≤i≤n,1≤Ti≤100\) 。
第 \(3\) 到 \(m+2\) 行,每行兩個整數 \(i\) 和 \(j\),用空格分隔,表示零件 \(i\) 要在零件 \(j\) 之前生產。
Output
第1行,1個整數,完成生產的最少時間。
第2行,1個整數,關鍵方案數,最多100位。
如果生產不能完成,只輸出1行,包含1個整數0.。
Sample Input
4 4
1 2 2 1
1 2
1 3
2 4
3 4
Sample Output
4
2
思路
題目翻譯過來就是求關鍵路徑條數和最短完成時間。首先因為他可能是一個不連通的圖,所以我們要引入虛源和虛匯來把他變成符合求關鍵路徑的模式(這點很重要!!);其次求路徑條數的過程中,我用的是計數原理,把每一點向後走關鍵路徑的個數統計下來,把所有的乘起來即可,要注意這道題的資料很大,要用高精乘;所求最短的時間即為求關鍵路徑中的 \(late[n+1]\)。
一定要注意:當你引入虛源和虛匯將其與其他點連邊之後,資料就變大了,陣列就不能只開 \(10^5\) 了!!yysy我在這裡被卡了一週...
程式碼
#include<cstdio>
#include<cctype>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#define rg register
#define ll long long
using namespace std;
inline int read(){
rg int f = 0, x = 0;
rg char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while( isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
return f? -x: x;
}
const int N = 200001;
const int M = 200001;
struct edge{
int nxt, to, w;
}e[M];
int tot = 0, head[M], n, m, deg[N] = {0}, cnt = 0, od[N] = {0};
int val[N];
queue <int > q;
int topo[N], critical[N], early[N] = {0}, late[N] = {0};
int out[N];
int a[200], b[200], c[200], len1 = 1, len2, len3;
inline void multi(int a[], int b[], int c[], int &len1, int &len2, int &len3){//b = a * n
len3 = len1 + len2 - 1;
for(int i = 1; i <= len2; ++i)
for(int j = 1; j <= len1; ++j){
c[i + j - 1] += a[j] * b[i];
c[i + j] += c[i + j - 1] / 10;
c[i + j - 1] %= 10;
}
if(c[len3 + 1] > 0) len3 ++;
}
inline void add(rg int u, rg int v, rg int w){
e[++tot].nxt = head[u];
e[tot].to = v;
e[tot].w = w;
head[u] = tot;
}
inline void toposort(){
q.push(0);
while(!q.empty()){
int u = q.front();
q.pop();
topo[++ cnt] = u;
for(rg int i = head[u]; i; i = e[i].nxt){
int v = e[i].to;
if(--deg[v] == 0) q.push(v);
}
}
}
inline void criticalroad(){
for(rg int i = 0; i <= n + 1; ++i) early[i] = 0;
for(rg int i = 1; i <= cnt; ++i){
int u = topo[i];
for(rg int j = head[u]; j; j = e[j].nxt){
int v = e[j].to;
early[v] = max(early[v], early[u] + e[j].w);
}
}
for(rg int i = 0; i <= n + 1; ++i) late[i] = early[n + 1];
for(rg int i = cnt; i >= 1; --i){
int u = topo[i];
for(rg int j = head[u]; j; j = e[j].nxt){
int v = e[j].to;
late[u] = min(late[u], late[v] - e[j].w);
}
}
cnt = 0;
for(rg int i = 0; i <= n + 1; ++i){
int u = i;
for(rg int j = head[u]; j; j = e[j].nxt){
int v = e[j].to;
int re = early[u];
int rl = late[v] - e[j].w;
if(re == rl){
out[u] ++;
}
}
}
}
signed main(){
n = read(), m = read();
for(rg int i = 1; i <= n; ++i){
val[i] = read();
}
for(rg int i = 1; i <= m; ++i){
int u = read(), v = read(), w = val[u];
add(u, v, w);
deg[v] ++;
od[u] ++;
}
for(rg int i = 1; i <= n; ++i){
if(!deg[i]) add(0, i, 0), deg[i] ++;
if(!od[i]) add(i, n + 1, val[i]), deg[n + 1] ++;
}
toposort();
if(cnt != n + 2){
cout << 0;
return 0;
}
criticalroad();
cout << late[n + 1] << endl;
a[1] = 1;
for(rg int i = 0; i <= n + 1; ++i){
if(!out[i] || out[i] == 1) continue;
else{
memset(c, 0, sizeof(c));
int u = out[i];
int tot = 0;
while(u){
b[++tot] = u % 10;
u /= 10;
}
len2 = tot;
multi(a, b, c, len1, len2, len3);
for(int j = 1; j <= len3; ++j) a[j] = c[j], len1 = len3;
}
}
for(rg int i = len1; i >= 1; --i){
printf("%d", a[i]);
}
return 0;
}
資料結構上機到此為止啦!!!