7-1 序列调度
原题呈现:
解题思路 : 该题思路很清晰,直接开一个栈对操作进行模拟即可。
以下是根据这一思路写出的代码:
#include<bits/stdc++.h>
using namespace std;
stack<int> s;
int num[10010];
int main()
{
int k, c, n, flag=1;
scanf("%d %d", &k, &c);
while (k--) {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &num[i]);
}
int i = 0;
for (int j = 1; j <= n; j++) {
while (!s.empty() && s.top() == num[i]) {
i++;
s.pop();
}
s.push(j);
if (s.size() > c) {
flag = 0;
break;
}
while (!s.empty() && s.top() == num[i]) {
i++;
s.pop();
}
}
while (i < n) {
if (s.top() == num[i]) {
i++;
s.pop();
}
else
break;
}
if (i == n && flag==1)
printf("Yes\n");
else
printf("No\n");
flag = 1;
}
}
7-2 最大最小差
原题呈现:
解题思路 : 不难发现规律:每次取最大的两个,得到的结果最小;每次取最小的两个,得到的结果最大。想实现每次取最大的两个,只要开一个数组然后用sort排一次序就行;想实现每次取最小的两个,建议直接调用STL中的priority_queue。
以下是根据这一思路写出的代码:
#include<bits/stdc++.h>
using namespace std;
long long v[20];
priority_queue<long long, vector<long long>, greater<long long>> pq;
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &v[i]);
pq.push(v[i]);
}
if (n != 1) {
while (pq.size() > 1) {
long long a = pq.top();
pq.pop();
long long b = pq.top();
pq.pop();
long long tmp = a * b + 1;
pq.push(tmp);
}
long long maxn = pq.top();
sort(v + 1, v + n + 1, less<long long>());
for (int i = n - 1; i >= 1; i--)
v[i] = v[i + 1] * v[i] + 1;
printf("%lld", maxn - v[1]);
}
else
printf("0");
}
解后反思: 1.考虑特殊情况n=1。 2.用long long。
7-3 二叉树最短路径长度
原题呈现:
解题思路:
1.用板子:由先根序列和中根序列建树。
2.走一遍先根遍历就能求根节点到其他所有结点的路径长度。
以下是根据这一思路写出的代码:
#include<bits/stdc++.h>
using namespace std;
struct tree {
int num;
int w;
struct tree* left;
struct tree* right;
};
tree* root;
int a[50000], b[50000], w[50000], nowi;
long long res[50000];
void build(tree*& t, int n)
{
if (nowi == n) return;
t = new tree;
t->num = a[nowi];
t->left = NULL;
t->right = NULL;
int j=0;
while (a[nowi] != b[j])
j++;
b[j] = 0;
for (int k = j - 1; k >= 0; k--)
{
if (b[k] == 0)
break;
if (b[k] == a[nowi + 1]){
nowi++;
build(t->left, n);
break;
}
}
for (int k = j + 1; k < n; k++){
if (b[k] == 0) break;
if (b[k] == a[nowi + 1]){
nowi++;
build(t->right, n);
}
}
}
void preorder(tree* t, long long dis)
{
if (t == NULL)
return;
dis += w[t->num];
res[t->num] = dis;
preorder(t->left, dis);
preorder(t->right, dis);
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
for (int i = 0; i < n; i++)
scanf("%d", &b[i]);
for (int i = 1; i <= n; i++)
scanf("%d", &w[i]);
build(root, n);
preorder(root, 0);
for (int i = 1; i <= n; i++){
printf("%lld", res[i]);
if (i != n)
printf(" ");
}
}
7-4 方案计数
原题呈现:
解题思路: 1.用动态规划结合拓扑排序求关键路径长度。2.正向扫一遍+逆向扫一遍确定关键活动。3.分层求出每一层有多少种方案,利用乘法原理求出关键方案总数(分层的方法详见注释)。
以下是根据这一思路写出的代码:
#include<bits/stdc++.h>
using namespace std;
//带前缀f的是逆向扫图以确定关键路径用的
vector<vector<int> > G, fG; //图
vector<int> in, fin;//入度数
queue<int> q, fq;
vector<int> length;
int dp[10010], fdp[10010], cnt, res[110];//dp、fdp用于动态规划,cnt用于纪录处理过的点的个数(判断生产能不能完成),res记录关键方案数
void gjcdj(int k){//高精度*单精度
int i;
for (i = 1; i <= res[0]; i++)
res[i] *= k;//每位先乘起来
for (i = 1; i <= res[0]; i++){
res[i + 1] += res[i] / 10;
res[i] %= 10;
}//处理进位
while (res[res[0] + 1] > 0){
res[0]++;
res[res[0] + 1] = res[res[0]] / 10;
res[res[0]] %= 10;
}//处理最高位相乘的进位
}
int main()
{
res[0] = 1;
res[1] = 1;
int ans = 0;//完成生产的最少时间
int n, m;
scanf("%d %d", &n, &m);
G = vector<vector<int> >(n + 1); //有n个点
fG = vector<vector<int> >(n + 1);
in = vector<int>(n + 1);
fin = vector<int>(n + 1);
length = vector<int>(n + 1);
for (int i = 1; i <= n; i++) {
scanf("%d", &length[i]);
}
for (int i = 1; i <= m; i++) {
int tmpx, tmpy;
scanf("%d %d", &tmpx, &tmpy);
G[tmpy].push_back(tmpx); //添加该点指向了什么点
fG[tmpx].push_back(tmpy); //添加该点指向了什么点
in[tmpx]++; //该点入度数+1
fin[tmpy]++;
}
for (int i = 1; i <= n; i++)
if (!in[i]) {
q.push(i);
dp[i] = length[i];
}//处理开始时入度为0的点
while (!q.empty()) {
int now = q.front();
q.pop();
cnt++;
for (int i = 0; i < G[now].size(); i++) {
int nex = G[now][i];
dp[nex] = max(dp[nex], dp[now] + length[nex]);//dp转移方程
in[nex]--;
if (in[nex] == 0)
q.push(nex);
}
}
for (int i = 1; i <= n; i++)
ans = max(ans, dp[i]);
int flag = -1, mul = 0;//flag借鉴bfs思想,用于判断“一层”的结束;mul是“一层”的方案数
for (int i = 1; i <= n; i++)
if (!fin[i]) {
fq.push(i);
fdp[i] = length[i];
}
while (!fq.empty()) {
int now = fq.front();
if (now == flag) {
gjcdj(mul);//乘法原理
mul = 0;
flag = -1;
}
mul++;
fq.pop();
for (int i = 0; i < fG[now].size(); i++) {
int nex = fG[now][i];
fdp[nex] = max(fdp[nex], fdp[now] + length[nex]);
fin[nex]--;
if (fin[nex] == 0 && dp[nex] + fdp[nex] - length[nex] == ans)
fq.push(nex);
if (flag == -1)
flag = nex;
}
}
if (m == 0) {//判断特殊情况
res[1]--;
for (int i = 1; i <= n; i++) {
if (dp[i] + fdp[i] - length[i] == ans)
res[1]++;
}
}
if (cnt == n) {
printf("%d\n", ans);
for (int i = res[0]; i >= 1; i--)
printf("%d", res[i]);
}
else
printf("0");
}
解后反思: 1.因为关键方案数最多可能有100位,因而要用到高精度*单精度。2.注意特判m=0的情形(测试点1)。