目录
题目一:7-1 序列调度 (100 分)
题目描述:
有一个N个数的序列A:1,2,……,N。有一个后进先出容器D,容器的容量为C。如果给出一个由1到N组成的序列,那么可否由A使用容器D的插入和删除操作得到。
输入格式:
第1行,2个整数T和C,空格分隔,分别表示询问的组数和容器的容量,1≤T≤10,1≤C≤N。
第2到T+1行,每行的第1个整数N,表示序列的元素数,1≤N≤10000。接下来N个整数,表示询问的序列。
输出格式:
T行。若第i组的序列能得到,第i行输出Yes;否则,第i行输出No,1≤i≤T。
输入样例:
在这里给出一组输入。例如:
2 2
5 1 2 5 4 3
4 1 3 2 4
输出样例:
在这里给出相应的输出。例如:
No
Yes
题目分析 :
这是一道栈模拟的题目。
并且题目序列也是由 1 到 N 组成,所以要求用栈来模拟是不能发生连续弹栈的行为。
数据结构准备方面需要准备一个栈和一个保存序列的数组,再用一个 top 来记录 序列A 的现行值。(因为A是从1到n入栈)
按照题目所述,每次的事件:
1.判断栈顶元素是否为所需值,是则弹出并 continue;
2.若当前栈顶元素不是所需的数,且序列元素的值小于 top ,说明要弹栈,这种情况是不允许的,因为弹栈需要删除元素,直接退出;
3.若所需值小于现行值,需要不断入栈直到其相等,在此之后要判断容量大小以及弹出一个;
算法效率方面:每个元素最多出入栈一次,时间复杂度为0(N);
代码:
#include <iostream>
#include <stack>
using namespace std;
const int maxn = 10001;
int c, t;
int a[maxn];
stack<int>st;
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
int flag = 0;
int m;
int top;
cin>>t>>c;
for (int i = 1;i<=t;++i)
{
while (!st.empty()) st.pop();
cin>>m;
for (int j = 1;j<=m;++j)
{
cin>>a[j];
}
top = 1;
flag = 0;
for (int j = 1;j<=m;++j)
{
if(!st.empty()&&st.top()==a[j])
{
st.pop();
continue;
}
if (top > a[j])
{
flag=1;
break;
}
while (top<=a[j])
{
st.push(top++);
}
if (int(st.size())>c)
{
flag = 1;
break;
}
st.pop();
}
if (flag) cout<<"No\n";
else cout<<"Yes\n";
}
return 0;
}
题目二:7-2 最大最小差 (100 分)
题目描述:
对n 个正整数,进行如下操作:每一次删去其中两个数 a 和 b,然后加入一个新数:a*b+1,如此下去直到 只剩下一个数。所有按这种操作方式最后得到的数中,最大的为max,最小的为min,计算max-min。
输入格式:
第1行:n,数列元素的个数,1<=n<=16。
第2行:n 个用空格隔开的数x,x<=10。
输出格式:
1行,所求max-min。
输入样例:
在这里给出一组输入。例如:
3
2 4 3
输出样例:
在这里给出相应的输出。例如:
2
题目分析:
此题目每次取出两个后放入一个,不难联想到这可以用Haffman树来做。
数据结构准备:两个堆(优先队列)
时间复杂度:O(N*logN);
本题应该全部用long long,不然在学校的OJ上过不了。
此题证明:
https://blog.csdn.net/GGN_2015/article/details/117375284
(看不懂啊)
代码:
#include <iostream>
#include <queue>
using namespace std;
priority_queue<int,vector<int> >q;
priority_queue<int,vector<int>,greater<int>>p;
int main()
{
int max,min;
int n,t;
int a,b;
cin>>n;
for (int i = 1;i<=n;i++)
{
cin>>t;
q.push(t);p.push(t);
}
for (int i = 1;i<n;i++)
{
a = q.top(); q.pop();
b = q.top(); q.pop();
t = a*b + 1;
q.push(t);
a = p.top(); p.pop();
b = p.top(); p.pop();
t = a*b + 1;
p.push(t);
}
min = q.top();
max = p.top();
cout<<max-min;
return 0;
}
注:在
题目三:7-3 二叉树最短路径长度 (100 分)
题目描述:
给定一棵二叉树T,每个结点赋一个权值。计算从根结点到所有结点的最短路径长度。路径长度定义为:路径上的每个顶点的权值和。
输入格式:
第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。
输出格式:
1行,n个整数,表示根结点到其它所有结点的最短路径长度。
输入样例:
在这里给出一组输入。例如:
4
1 2 4 3
4 2 1 3
1 -1 2 3
输出样例:
在这里给出相应的输出。例如:
1 0 3 3
题目分析:
此题给定一个先根序列以及一个中根序列要求建造出原来的二叉树,至于求最短路,只需要二叉树遍历一遍即可。难点在于建出二叉树。
数据结构准备:静态链表(跳舞链)
建造二叉树以一种递归的形式,锁定先根序列的位置以及对应的中根序列的位置,对中根序列进行遍历,找到后再次锁定,递归进行。递归出口为空指针时(锁定的先根序列的位置不合法)
时间复杂度:O()
int create(int prel,int prer,int midl,int midr)
{
if (prel>prer)
{
return 0;
}
int pret = pre[prel];
int pmidt = Find(pret,midl,midr);
l[pret] = create(prel+1,prel + pmidt - midl,midl,pmidt-1);
r[pret] = create(prel + pmidt - midl +1,prer,pmidt + 1,midr);
return pret;
}
代码:
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 20002;
const int INF = 999999999;
int n;
typedef struct Node
{
int to,cost;
}link;
int dist[maxn];
int l[maxn],r[maxn];
int pre[maxn]; //前序
int mid[maxn]; //中序
inline int Find(int t,int midl,int midr)
{
for (int i = midl;i<=midr;i++)
{
if (mid[i] == t)
return i;
}
}
int create(int prel,int prer,int midl,int midr)
{
if (prel>prer)
{
return 0;
}
int pret = pre[prel];
int pmidt = Find(pret,midl,midr);
l[pret] = create(prel+1,prel + pmidt - midl,midl,pmidt-1);
r[pret] = create(prel + pmidt - midl +1,prer,pmidt + 1,midr);
return pret;
}
void LIO(int n)
{
cout<<n<<' ';
if (r[n])
{
LIO(r[n]);
}
if (l[n])
{
LIO(l[n]);
}
}
void dfs(int head)
{
if (l[head])
{
dist[l[head]] += dist[head];
dfs(l[head]);
}
if (r[head])
{
dist[r[head]] += dist[head];
dfs(r[head]);
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
int head;
for (int i = 1;i<=n;i++)
{
cin>>pre[i];
}
for (int i = 1;i<=n;i++)
{
cin>>mid[i];
}
for (int i = 1;i<=n;i++)
{
cin>>dist[i];
}
head = create(1,n,1,n);
dfs(head);
for (int i = 1;i<n;i++)
cout<<dist[i]<<' ';
cout<<dist[n];
return 0;
}
题目四:7-4 方案计数 (100 分)
题目描述:
组装一个产品需要 n 个零件。生产每个零件都需花费一定的时间。零件的生产可以并行进行。有些零件的生产有先后关系,只有一个零件的之前的所有零件都生产完毕,才能开始生产这个零件。如何合理安排工序,才能在最少的时间内完成所有零件的生产。在保证最少时间情况下,关键方案有多少种,关键方案是指从生产开始时间到结束时间的一个零件生产序列,序列中相邻两个零件的关系属于事先给出的零件间先后关系的集合,序列中的每一个零件的生产都不能延期。
输入格式:
第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之前生产。
输出格式:
第1行,1个整数,完成生产的最少时间。
第2行,1个整数,关键方案数,最多100位。
如果生产不能完成,只输出1行,包含1个整数0.
输入样例:
在这里给出一组输入。例如:
4 4
1 2 2 1
1 2
1 3
2 4
3 4
输出样例:
在这里给出相应的输出。例如:
4
2
题目分析:
注意高精度,方案数100位
方法一:
此题可用拓扑排序 + 动态规划,以一种递推的方式。
数据结构准备:高精度方案数 cases[maxn],图,生产完成时间数组 ve[maxn],生产时间数组 cost[maxn]
1.如果一个点的生产完成时间要晚于该点的一个前序点的生产完成时间与前序点生产之和,那么该点生产完成时间应该更新并且方案数与前序点的方案数相同;
if (ve[t]<ve[j] + cost[t])
{
ve[t] = ve[j] + cost[t];
cases[t] = cases[j];
}
2.如果一个点的生产完成时间要与该点的一个前序点的生产完成时间与前序点生产之和相同,那么该点方案数应该加上与前序点的方案数;
else if (ve[t] == ve[j] + cost[t])
{
cases[t] = cases[j] + cases[t];
}
动态规划 + 拓扑排序完成之后,应在图中寻找生产完成时间的最大值(不唯一),即最后生产的零件,答案为这些零件的方案数之和;
总体时间复杂度:O(N+E)
代码:
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 105;
class bignum
{
int digit;
bool f;
int n[maxn];
void invert();
public:
bignum();
bignum operator = (const int num);
bignum operator * (const int num);
friend istream& operator >>(istream& in,bignum& a);
friend ostream& operator <<(ostream& out,bignum &a);
friend bignum operator + (const bignum a,const bignum b);
};
bignum bignum::operator = (const int num)
{
int tmp = num;
int cnt = 0;
while (tmp)
{
n[++cnt] = tmp%10;
tmp /= 10;
}
digit = cnt;
invert();
return *this;
}
bignum bignum::operator * (const int num)
{
int tmp = num;
bignum a;
for (int i = 1;i<=tmp;i++)
{
a = a + *this;
}
return a;
}
ostream& operator <<(ostream& out,bignum& a)
{
for (int i = 1;i<=a.digit;i++)
{
out<<a.n[i];
}
if (a.digit == 0)
out<<"0";
return out;
}
istream& operator >>(istream& in,bignum& a)
{
char s[maxn];
in>>s;
a.digit = strlen(s);
int i = 0;int j = 1;
if (s[0] == '-')
{
a.f = 1;i++;a.digit--;
}
while (s[i] == '0')
{
i++;
}
for (;i<a.digit;i++)
{
a.n[j++] = s[i] - '0';
}
a.digit = j-1;
return in;
}
bignum::bignum()
{
for (int i = 0;i<=maxn;i++)
n[i] = 0;
digit = 0;
f = 0;
}
void bignum::invert()
{
for (int i = 1;i<=digit/2;i++)
swap(n[i],n[digit-i+1]);
}
bignum operator + (const bignum a,const bignum b)
{
bignum c,a1,b1;
a1 = a;b1 = b;
int t = 0;
int len = max(a1.digit,b1.digit);
a1.invert();b1.invert();
for (int i = 1;i<=len;i++)
{
c.n[i] = (a1.n[i] + b1.n[i] + t)%10;
t = (a1.n[i] + b1.n[i] + t)/10;
}
c.digit = len;
if (t)
{
c.digit++;
c.n[c.digit] = t;
}
c.invert();
return c;
}
const int INF = 9999999;
const int MAXN = 10001;
vector<int>a[MAXN];
ll ve[MAXN];
int tpo[MAXN],in[MAXN];
ll cost[MAXN];
bignum cases[MAXN];
int tp(int n)
{
vector<int>st;
int j,cnt = 0;
for (int i = 0;i<=n;i++)
sort(a[i].begin(),a[i].end());
for (int i = 0;i<=n;i++)
ve[i] = 0;
for (int i = 1;i<=n;i++)
{
if (!in[i])
{
st.push_back(i);
cases[i] = 1;
ve[i] = cost[i];
}
}
sort(st.begin(),st.end(),greater<int>());
for (int i = 1;i<=n;i++)
{
if (st.empty())
return 0;
j = st.back();st.pop_back();
tpo[++cnt] = j;
for (int k = 0;k<int(a[j].size());k++)
{
int t = a[j][k];
in[t]--;
if (in[t] == 0)
{
st.push_back(t);
}
if (ve[t]<ve[j] + cost[t])
{
ve[t] = ve[j] + cost[t];
cases[t] = cases[j];
}
else if (ve[t] == ve[j] + cost[t])
{
cases[t] = cases[j] + cases[t];
}
}
}
return 1;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int n,m,t1,t;
cin>>n>>m;
for (int i = 1;i<=n;i++)
{
cases[i] = 0;
in[i] = 0;
}
for (int i = 1;i<=n;i++)
{
cin>>cost[i];
}
for (int i = 1;i<=m;i++)
{
cin>>t1>>t;
a[t1].push_back(t);
in[t]++;
}
for (int i = 1;i<=n;i++)
{
if (!in[i])
a[0].push_back(i);
}
if (tp(n)==0)
{
cout<<"0";
return 0;
}
int max = -INF;
int pend;
int cnt = 0;
bignum ans;
for (int i = 1;i<=n;i++)
{
if (max<ve[i])
{
pend = i;
cnt = 1;
max = ve[i];
ans = cases[i];
}
else if (max == ve[i])
{
cnt++;
ans = ans + cases[i];
}
}
cases[pend] = cases[pend]*cnt;
cout<<ve[pend]<<'\n'<<ans;
return 0;
}
方法二:
拓扑排序 + 关键活动/路径
需要建立虚源点、虚汇点。
for (int i = 1;i<=n;i++) //虚源点
{
if (!in[i])
{
st.push_back(i);
link t = {i,cost[i]};
a[0].push_back(t);
}
}
for (int i = 1;i<=n;i++) //虚汇点
{
if (!a[i].size())
{
a[i].push_back({n+1,0});
in[n+1]++;
}
}
生产最早时间:
void early(int n)
{
int j,k;
for (int i = 0;i<=n+1;i++) //cp2 最早时间
ve[i] = 0;
for (int i = 0;i<=n;i++)
{
j = tpo[i];
for (k = 0;k<int(a[j].size());k++)
{
ve[a[j][k].to] = max(ve[a[j][k].to],ve[j] + a[j][k].cost);
}
}
}
生产最迟时间:
void late(int n)
{
int j,k;
for (int i = 0;i<=n+1;i++)
vl[i] = ve[n+1];
for (int i = n;i>=0 ;i--)
{
j = tpo[i];
for (k = 0;k<int(a[j].size());k++)
{
vl[j] = min(vl[j],vl[a[j][k].to] - a[j][k].cost);
}
}
}
根据拓扑序求关键路径:
void keyR(int n)
{
int j,e,l;
int k;
cases[0] = 1;
for (int u = 0;u<=n+1;u++)
{
int i = tpo[u];
for (j = 0;j<int(a[i].size());j++)
{
k = a[i][j].to;
e = ve[i];
l = vl[k] - a[i][j].cost;
if (l == e)
{
cases[k] = cases[i] + cases[k];
}
}
}
}
总体时间复杂度为O(N+E)
代码:
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 110;
class bignum
{
int digit;
bool f;
int n[maxn];
void invert();
public:
bignum();
friend istream& operator >>(istream& in,bignum& a);
friend ostream& operator <<(ostream& out,bignum &a);
friend bignum operator + (const bignum a,const bignum b);
bignum operator = (const int num);
bignum operator * (const int num);
bignum operator += (const bignum a);
};
bignum bignum::operator = (const int num)
{
int tmp = num;
int cnt = 0;
while (tmp)
{
n[++cnt] = tmp%10;
tmp /= 10;
}
digit = cnt;
invert();
return *this;
}
bignum bignum::operator * (const int num)
{
int tmp = num;
bignum a;
for (int i = 1;i<=tmp;i++)
{
a = a + *this;
}
return a;
}
ostream& operator <<(ostream& out,bignum& a)
{
for (int i = 1;i<=a.digit;i++)
{
out<<a.n[i];
}
if (a.digit == 0)
out<<"0";
return out;
}
istream& operator >>(istream& in,bignum& a)
{
char s[maxn];
in>>s;
a.digit = strlen(s);
int i = 0;int j = 1;
if (s[0] == '-')
{
a.f = 1;i++;a.digit--;
}
while (s[i] == '0')
{
i++;
}
for (;i<a.digit;i++)
{
a.n[j++] = s[i] - '0';
}
a.digit = j-1;
return in;
}
bignum::bignum()
{
for (int i = 0;i<=maxn;i++)
n[i] = 0;
digit = 0;
f = 0;
}
void bignum::invert()
{
for (int i = 1;i<=digit/2;i++)
swap(n[i],n[digit-i+1]);
}
bignum operator + (const bignum a,const bignum b)
{
bignum c,a1,b1;
a1 = a;b1 = b;
int t = 0;
int len = max(a1.digit,b1.digit);
a1.invert();b1.invert();
for (int i = 1;i<=len;i++)
{
c.n[i] = (a1.n[i] + b1.n[i] + t)%10;
t = (a1.n[i] + b1.n[i] + t)/10;
}
c.digit = len;
if (t)
{
c.digit++;
c.n[c.digit] = t;
}
c.invert();
return c;
}
bignum bignum::operator += (const bignum num)
{
*this = *this + num;
return *this;
}
const int MAXN = 10002;
typedef struct node
{
int to,cost;
}link;
bool cmp(link a,link b)
{
if (a.to>b.to)
return 1;
else
return 0;
}
vector<link>a[MAXN];
int ve[MAXN],vl[MAXN];
int tpo[MAXN],in[MAXN];
int cost[MAXN];
bignum cases[MAXN];
int tp(int n)
{
vector<int>st;
int j,cnt = 0;
for (int i = 1;i<=n;i++)
sort(a[i].begin(),a[i].end(),cmp);
for (int i = 1;i<=n;i++)
{
if (!in[i])
{
st.push_back(i);
link t = {i,cost[i]};
a[0].push_back(t);
}
}
sort(st.begin(),st.end(),greater<int>());
for (int i = 1;i<=n;i++)
{
if (st.empty())
return 0;
j = st.back();st.pop_back();
tpo[++cnt] = j;
for (int k = 0;k<int(a[j].size());k++)
{
in[a[j][k].to]--;
if (in[a[j][k].to] == 0)
{
st.push_back(a[j][k].to);
}
}
}
return 1;
}
void early(int n)
{
int j,k;
for (int i = 0;i<=n+1;i++) //cp2 最早时间
ve[i] = 0;
for (int i = 0;i<=n;i++)
{
j = tpo[i];
for (k = 0;k<int(a[j].size());k++)
{
ve[a[j][k].to] = max(ve[a[j][k].to],ve[j] + a[j][k].cost);
}
}
}
void late(int n)
{
int j,k;
for (int i = 0;i<=n+1;i++)
vl[i] = ve[n+1];
for (int i = n;i>=0 ;i--)
{
j = tpo[i];
for (k = 0;k<int(a[j].size());k++)
{
vl[j] = min(vl[j],vl[a[j][k].to] - a[j][k].cost);
}
}
}
void keyR(int n)
{
int j,e,l;
int k;
cases[0] = 1;
for (int u = 0;u<=n+1;u++)
{
int i = tpo[u];
for (j = 0;j<int(a[i].size());j++)
{
k = a[i][j].to;
e = ve[i];
l = vl[k] - a[i][j].cost;
if (l == e)
{
cases[k] = cases[i] + cases[k];
}
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int n,m,t1;link t;
cin>>n>>m;
for (int i = 1;i<=n;i++)
{
in[i] = 0;
cases[i] = 0;
cin>>cost[i];
}
for (int i = 1;i<=m;i++)
{
cin>>t1>>t.to;
t.cost = cost[t.to];
a[t1].push_back(t);
in[t.to]++;
}
for (int i = 1;i<=n;i++)
{
if (!a[i].size())
{
a[i].push_back({n+1,0});
in[n+1]++;
}
}
if (tp(n)==0) //cp1 拓扑序列
{
cout<<"0";
return 0;
}
early(n);
late(n);
keyR(n);
cout<<ve[n+1]<<'\n'<<cases[n+1];
return 0;
}