一、序列调度
题目
有一个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,2,……,N这个序列压入栈中,每一遍循环压入一个元素,同时在这遍循环中压入元素之后将栈顶元素与要得到的序列进行比较若要相同则出栈,否则继续下一次循环直至进行了N次循环,若栈空则可以得到输出“Yes”,否则不可以得到输出“No”。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 10001;
stack<int> s;
int a[maxn];
int main(){
//while(!s.empty()) s.pop();
int T,C;
int flag;
scanf("%d%d",&T, &C);
while(T){
int N;
scanf("%d",&N);
for(int i = 1; i <= N; i++){
scanf("%d",&a[i]);
}
int j = 1;
for(int i = 1; i <= N; i++){
if(s.size() < C ) s.push(i);
else break;
while(1){
if(s.top() == a[j]) {
s.pop();
j++;
}
if(s.empty() || s.top() != a[j]) break;
}
}
if(s.empty()) printf("Yes\n");
else printf("No\n");
T--;
}
return 0;
}
二、最大最小差
题目
对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
解题思路
这道题其实有一个规律,就是如果要求 max 那么就每次从整数中抽取两个最小的进行计算;同理,若要求 min 那么就每次从整数中抽取两个最大的只进行计算即可得到。
由于每次不是取最小就是取最大所以,我使用了优先级队列来实现取最大最小操作。
参考代码
#include<bits/stdc++.h>
using namespace std;
int min;
priority_queue<int,vector<int>, less<int> > pq1;// 使用递增less<int>函数对象排序
priority_queue<int,vector<int>, greater<int> > pq2;
int main(){
int n;
scanf("%d",&n);
int a;
while(!pq1.empty()) pq1.pop();
while(!pq2.empty()) pq2.pop();
for(int i = 1; i <= n; i++){
scanf("%d", &a);
pq1.push(a);
pq2.push(a);
}
int min, max;
for(int i = 1; i <= n-1; i++){
a = pq1.top(); pq1.pop();
min = a*pq1.top() + 1; pq1.pop();
pq1.push(min);
a = pq2.top(); pq2.pop();
max = a*pq2.top() + 1; pq2.pop();
pq2.push(max);
}
printf("%d",max-min);
return 0;
}
三、二叉树最短路径长度
题目
给定一棵二叉树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
解题思路
由于是二叉树所以每两个根节点到其他节点只有一条路径,即求最短长度很简单,难点就在于建树,题目给的是先根和中根,即可以先根据先根序确定根再到中根序中找到根节点将中根序分为左右子树,再次递归求解即可建树完毕。
参考代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 20001;
int wei[maxn];
int len[maxn];
int pre[maxn];//先根
int in[maxn];//中根
typedef struct BiNode
{
int date;
BiNode* LChild;
BiNode* RChild;
}*BiTree;
void CreateBiTree(BiTree &root,int preLeft, int preRight, int inLeft, int inRight)
{
if (preLeft > preRight) root=NULL;
else
{
root = new BiNode;
int mid = -1;
for (int i = inLeft; i <= inRight; i++)
{
if (in[i] == pre[preLeft])
{
mid = i;
break;
}
}
root->date = in[mid];
CreateBiTree(root->LChild,preLeft + 1, preLeft + mid - inLeft, inLeft, mid - 1);
CreateBiTree(root->RChild,preRight + mid - inRight + 1, preRight, mid + 1, inRight);
}
}
void solute(BiTree root){
if(root->LChild!=NULL) { len[root->LChild->date] = len[root->date] + wei[root->LChild->date]; solute(root->LChild); }
if(root->RChild!=NULL) { len[root->RChild->date] = len[root->date] + wei[root->RChild->date]; solute(root->RChild); }
}
int main(){
int n;
scanf("%d",&n);
for(int i = 0; i < n; i++){
scanf("%d",&pre[i]);
}
for(int i = 0; i < n; i++){
scanf("%d",&in[i]);
}
for(int i = 1; i <= n; i++){
scanf("%d",&wei[i]);
}
int preLeft=0;int preRight=0;int inLeft=0;int inRight;
preRight = inRight = n - 1;
BiTree root=new BiNode;
CreateBiTree(root,preLeft,preRight,inLeft,inRight);
len[root->date] = wei[root->date];
solute(root);
for(int i = 1; i <= n; i++) {
printf("%d",len[i]);
if(i!=n) printf(" ");
}
return 0;
}
四、方案计数
题目
组装一个产品需要 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
解题思路
首先我们看出来这是一道求关键路径的题目,但是由于关键路径的算法是对于AOE网的,但是这个题目给出的是AOV网,所以需要将AOV网转换为AOE网;课上讲过的转换方法有两种分别是:1)对每个点v引入镜像点v’,点权放到新增边<v,v’>上;原来的边<u,v>转换为<u’,v>。2)引入一个虚源点和虚汇点。(我用的是后者)。
再有一个问题就是方案数可能高达一百位,所以需要使用高精度计算;
接下来就是代码的具体实现了
参考代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
const int maxn = 100001;
vector<pair<int, int> > vec[maxn]; //用来存储比该点要之后生产的节点及路径的权值
int n, m;
int cost[maxn]; //零件生产花费的时间
int count1[maxn];//每个顶点的入度
int tor[maxn];
int ve[maxn],vl[maxn];//ve[]是最早发生时间,vl[]是最迟发生时间
int len = 1;//方案数的位数
int ans[101];//方案数
void TopoOrder(){// 拓扑排序算法:AOV网
stack<int> S;
S.push(0);
int k = 0;
for(int i = 0; i <= n + 1; i++){
if(S.empty()) {printf("0"); exit(0);}
int j=S.top(); S.pop();
tor[k++] = j;
vector<pair<int, int> >::iterator it;
for(it = vec[j].begin(); it != vec[j].end(); it++){
count1[it->first] --;
if(count1[it->first] == 0) S.push(it->first);
}
}
}
void CriticalPath(){//关键路径:AOE网
TopoOrder();
//计算事件的最早发生时间
for(int i = 0; i <= n; i++) ve[i]=0;
vector<pair<int, int> >::iterator it;
for(int i = 1; i <= n; i++){
for(it = vec[tor[i]].begin(); it != vec[tor[i]].end(); it++){
if(ve[tor[i]] + it->second > ve[it->first])
ve[it->first] = ve[tor[i]] + it->second;
}
}
//计算事件最迟发生时间
for(int i = 0; i <= n + 1; i++) vl[i]=ve[n + 1];
for(int i = n; i >= 0; i--){
for(it = vec[tor[i]].begin(); it != vec[tor[i]].end(); it++){
if(vl[it->first] - it->second < vl[tor[i]])
vl[tor[i]] = vl[it->first] - it->second;
}
}
}
void address(){
int u, v;
for(int i = 0; i < m; i++){
scanf("%d%d",&u, &v);
vec[u].push_back(make_pair(v,cost[u]));
count1[v]++;
}
//加入虚源点与虚汇点
for(int i = 1; i <= n; i++)
{
if(count1[i] == 0){
vec[0].push_back(make_pair(i,0));
count1[i]++;
}
}
for(int i = 1; i <= n; i++){
if(vec[i].size() == 0){
vec[i].push_back(make_pair(n+1,cost[i]));
count1[n+1]++;
}
}
}
void mul(int num){
int x = 0,temp;// x 是进位; temp 是当前位与其乘积的结果
int j = 1;
while(j <= len){
temp = ans[j-1]*num + x;
ans[j-1] = temp%10;
x = temp/10;
j++;
}
while(x){
ans[len] = x % 10;
len++;
x = x / 10;
}
}
void Cou(){
for(int i = 0; i <= n; i++){
int sum = 0;
vector<pair<int,int> >::iterator it;
for(it = vec[i].begin(); it != vec[i].end(); it++){
//k=(*it).first;
int vee = ve[i];
int vll = vl[it->first]-it->second;
if(vll == vee){
sum++;
}
}
if(sum != 0) mul(sum);
}
}
int main(){
ans[0] = 1;
scanf("%d%d",&n, &m);
for(int i = 1; i <= n; i++){
scanf("%d",&cost[i]);
}
address();
CriticalPath();
Cou();
printf("%d\n",vl[n + 1]);
for(int i = len-1; i >= 0; i--) printf("%d",ans[i]);
return 0;
}