1. 程式人生 > 其它 >【JLU資料結構榮譽課】第七次上機實驗

【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;
}

資料結構上機到此為止啦!!!