文章目录
PAT(主要的套路模板及刷题总结)
主要参考算法笔记(晴神宝典)和柳婼的博客
//经常用到的头文件
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#include <bits/stdc++.h>//包含上述所有的头文件
一、基础知识
-
b1018 有时直接输出结果更简单,不需要寄存结果
-
A1046上述情况的反例,有时需要预处理才能避免超时
-
string 类不能存放字符类型以外的类型 // 如果放入整数就会出问题
-
%02d 不够两位则左边补零
-
string t = s.substr(1, n);//子串位置1开始截取,长度为n int n = stoi(s.substr(i+1));//stoi字符串计算,转化为int型如果溢出则报错
-
while(scanf("%d",&n)!=EOF)//EOF表示文件结束即-1,scanf读不到数据将返回-1;
-
`int main() {
char c[50] = “1123abc1”;
int a;
sscanf(c, “%d”, &a); // 不要忘记 “&” 将字符数组最前面的整数放入a
int b = 567;
sprintf(c, “%d”, b);//清空字符数组,再将整数b的值放到字符数组中
cout << a << endl << c;return 0;//练习题见PAT1008
}`
二、排序、贪心、二分
排序传参建议用引用传参,这样更快,虽然有时候不用引用传参也能通过,但还是尽量用,养成好习惯~
8. A1062越简单的题目越要注意输入输出的选择以免超时。特别时数据量较大时。优先选择c语言的输入命令
9. for (auto it : parktime) ;//这里it被auto推断为map<int,int>::iterator类型,它是迭代器的内容故不需要加* it!=m.end();++it)
3. A1016 有时候关于时间**(日期)单独计算很难是,转化思路找一个起始点计算(特别是需要相减的问题)。一般关于状态类的最好用bool类型表示哪怕题目给出的是字符串。(注意:sort要对结构体进行排序最好用迭代器)
4. 能用fill赋值就不用memset赋值,后者仅用于赋值0,1了
5. A1075最后一个测试点, 没有通过编译覆盖最高分
6. A1067是真的坑!!!用for循环会超时,所以之后若超时了但在算法无法改进的情况下改为while试试,最好习惯用while
7.
8. 二分法一般是对有序的数组**进行选择
9. A1089 二分查找
转自柳神博客
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int n, a[100], b[100], i, j;
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
for (int i = 0; i < n; i++)
cin >> b[i];
for (i = 0; i < n - 1 && b[i] <= b[i + 1]; i++);
for (j = i + 1; a[j] == b[j] && j < n; j++);
if (j == n) {
cout << "Insertion Sort" << endl;
sort(a, a + i + 2);
} else {
cout << "Merge Sort" << endl;
int k = 1, flag = 1;
while(flag) {
flag = 0;
for (i = 0; i < n; i++) {
if (a[i] != b[i])
flag = 1;
}
k = k * 2;
for (i = 0; i < n / k; i++)
sort(a + i * k, a + (i + 1) * k);
sort(a + n / k * k, a + n);
}
}
for (j = 0; j < n; j++) {
if (j != 0) printf(" ");
printf("%d", a[j]);
}
return 0;
}
- 如果数组固定长度在main函数外确定,那么数组也要在main函数外定义
- 格式错误有可能是少输出一个换行
- A1104 大量浮点数相加精度问题,可以用先转long long(有可能需要进行先乘后除的操作)
- 有理数运算的一些模板
struct fraction {
ll up, down;
}f;
int gct(ll a, ll b) {
return (a%b == 0) ? b : gct(b, a%b);
}
fraction reduction(fraction f) {
if (f.down < 0) {
f.down = -f.down;
f.up = -f.up;
}
if (f.up == 0) f.down = 1;
else {
int d = gct(abs(f.down), abs(f.up));
f.up /= d;
f.down /= d;
}
return f;
}
fraction add(fraction f1, fraction f2) {
fraction sum;
sum.down = f1.down*f2.down;
sum.up = f1.up * f2.down + f2.up*f1.down;
return reduction(sum);
}
void showfraciton(fraction f) {//输出分数
reduction(f);
if (f.down == 1) {
if(f.up< 0) printf("(%lld)", f.up);
else printf("%lld", f.up);
}
else if (abs(f.up) > f.down) {
//假分数
if(f.up<0) printf("(%lld %lld/%lld)", f.up / f.down, abs(f.up) % f.down, f.down);
else printf("%lld %lld/%lld", f.up / f.down, abs(f.up) % f.down, f.down);
}
else {
if(f.up<0) printf("(%lld/%lld)", f.up, f.down);
printf("%lld/%lld", f.up, f.down);
}
- 大整数运算主要模板
struct bign {
int d[1000];
int len;
bign() {
memset(d, 0, sizeof(d));
len = 0;
}
};
bign change(string str) {
bign a;
for (int i = 0; i < a.len; ++i) {
a.d[a.len++] = str[str.size() - 1 - i] - '0';//高位在数组高位时为了方便之后的运算
}
return a;
}
int compare(bign a, bign b) {
if (a.len > b.len) return 1;
else if (a.len < b.len) return -1;
else {
for (int i = a.len - 1; i >= 0; --i) {
if (a.d[i] > b.d[i]) return 1;
else if(a.d[i]<b.d[i]) return -1;
}
}
return 0;
}
bign Mutil(bign a, int b) {
bign c;
int carry = 0;
for (int i = 0; i < a.len; ++i) {
int temp = a.d[i] * b + carry;
c.d[c.len++] = temp % 10;
carry = temp / 10;
}
while (carry != 0) {
c.d[c.len++] = carry % 10;
carry /= 10;//循环是因为caryy可能不止一位
}
return c;
}
bign sub(bign a, bign b) {
//a》b前者是被减数
bign c;
for (int i = 0; i < a.len; ++i) {
if (a.d[i] < b.d[i]) {
a.d[i + 1]--;
a.d[i] += 10;
}
c.d[c.len++] = a.d[i] - b.d[i];
}
while (c.len >= 2 && c.d[c.len - 1] == 0) {
//清除高位为0的
c.len--;
}
return c;
}
doubel p = 1e12;//10^12
- upper_bound(起始地址,结束地址,要查找的数值) 返回的是数值 第一个大于查找值的位置
binary_search(起始地址,结束地址,要查找的数值) 返回的是是否存在这么一个数,返回bool值 - 计算中缀表达式
1.首先中缀转后缀表达式
1.设置操作符栈保存操作符;设置队列或数组保存后缀表达式
2.从左至右扫描中缀表达式,如果碰到操作数,将其加入后缀表达式
3.如果碰到操作符
1.op优先级高于栈顶操作符的优先级,就压入栈(因为优先级高的要在后缀表达式的前面以便先计算,也就是更靠近栈顶)
2.否则就不断弹出操作符到后缀表达式中直到op的优先级高于栈顶操作符的优先级
4.重复直到扫描完毕,zhi后如栈还有元素,则将它们依次弹出至表达式中、
操作符的优先级可以用map建立操作符的优先级的映射
2.计算后缀表达式:如果时操作数就压入栈;如果时操作符就弹出两个操作数进行计算生成的结果压入栈中
三、STL标准模板库
- 除了vector和string之外的stl容器都不支持*(it + i)的访问方式;set容器内元素是有序不重复的,插入需要用insert(),而且只支持按值查找find()。当有两个连续的’>‘,需用空格隔开以防止误认为是右移操作。
- auto 用法对比
//转自[柳神](https://blog.csdn.net/liuchuo/article/details/52138993)
for(auto it = v[a-1].begin(); it != v[a-1].end(); it++) {
if(v[b-1].find(*it) == v[b-1].end())
nt++;
else
nc++;
}
//本人手写
for (auto it : s[i]) {//it是一个一个的值,但是它并不同于那个值,因为它是迭代器
if (s[j].find(it) != s[j].end()) {
nt--; nc++;
}
}
//转义符%
printf("%.1f%%\n", (nc*100.0) / nt );//如果要输出%需要在其前面加上%
- 用字符串记录浮点数的一些注意点:1.去除前导零 2.看小数点位置并且要注意小数点后只有零的情况。3.如果空了就补零
- C++11标准增加了**unordered_map **其不按键排序,因此速度远快于 map
- getline(cin,string)//遇到换行停止输入但是不把换行存入
- **函数参数用引用快于传值参数,特别是在有大量数据时应该运用。**最好能养成善用引用的好习惯
- s.c_str()将string类转化为字符数组,可用于scanf操作如
scanf("%s",str.c_str())
#include<cctype>
10. `isalnum()`是字母或数字。
11. `isalpha()`是字母。
12. `isdigit()`是数字。
13. `isupper()`是大写字母。
14. `islower()`是小写字母。
15. `isblank()`是空格/tab键。
16. `isspace()`是空格/tab/回车。
原文链接:https://blog.csdn.net/u013764814/article/details/100597926
-
for循环只能用于有begin的容器
-
vector<pair<int,int> > a //存储的是一对一对的没有 a.push_back(make_pair(i,j));
-
transform(temp1.begin(), temp1.end(), back_inserter(temp), ::tolower);//转化为小写;
-
优先队列
struct Node {
int q, num = 0;
Node(int a,int b) : q(a), num(b){}
bool operator < (const Node &a) const {//优先级更高
return (a.num != num) ? num > a.num : q < a.q;
}
};`
21.count() 用来查找set中某个某个键值出现的次数。这个函数在set并不是很实用,因为一个键值在set只可能出现0或1次,这样就变成了判断某一键值是否在set出现过了。map也有类似的,不过查找的是键而不是值
21. stl容器反向遍历
for (auto it = ans.rbegin(); it != ans.rend(); ++it) {
cout << *it << endl;
}
/*auto it:数据结构 注意两者区分
## 四、链表
1. vs2017堆栈溢出异常, 原因:申请内存过大。解决方案:项目属性 配置属性 链接器 系统堆**堆栈保留大小**(输入数字即可,单位为kb)
2. 链表题用静态的数组会更简单。。
```cpp
struct Node {
int next, data;//几乎必用的
int order;//主要用于遍历以及去除无效节点
bool flag;//通常结合order使用
}node[maxn];
五、DFS和BFS两种搜索方式
- DFS//递归
适用场景:给定一个序列,枚举这个序列的所有子序列(可以不连续如背包问题)等价于枚举从N个整数选择K个数的所有方案
注意“剪枝”方法,即当满足一定条件后才选择走这条岔路口
void dfs(int index, int facsum, int nownum, int sqrsum) {
if (sqrsum == n && nownum == k) {
//递归边界 满足题设条件的地方需要进行更新
if (facsum > MAXfacsum) {
MAXfacsum = facsum;
ans = temp;
}
return;
}
if (sqrsum > n || nownum > k) return;//不满足条件需要回退剪枝
if (index - 1 >= 0) {
//还可以加数的
temp.push_back(index);
dfs(index, facsum + index, nownum + 1, sqrsum + fac[index]);//加入 fac[i]等于i^p;
temp.pop_back();//回退需要弹出之前的元素
dfs(index - 1, facsum, nownum, sqrsum);//不加入
}
}
- BFS//一般由队列来实现
void BFS(int s){
queue<int> q;
q.push(s);
//设置已入队
while(!q.empty()) {
取出队首元素top;
访问元素top
将队首元素出队
将top下一层节点中未曾入队的节点全部入队,并设置为已入队//并标记它们的层号加1
}
}
六、树
- 普通二叉树一般用指针节点,而完全二叉树用数组较好
- 新生成指针节点需要加new
- 判断是不是完全二叉树,就看在出现了一个孩子为空的结点之后是否还会出现孩子结点不为空的结点,如果出现了就不是完全二叉树。
- 二叉树模板
struct Node {
int data;
Node* lchild;
Node* rchild;
};//对应的也有静态写法
Node* create(int postl, int postr, int inl, int inr) {
if (inl > inr) return NULL;
Node* root = new Node;//一定要加new动态分配内存
root->data = post[postr];
int k;
for (int i = 1; i <= n; ++i) {
if (inorder[i] == root->data) {
k = i; break;
}
}
int numleft = k - inl;
root->lchild = create(postl, postl + numleft - 1, inl, inl + numleft - 1);
root->rchild = create(postl + numleft, postr - 1, inl + numleft + 1, inr);
return root;
}
void bfs(Node* root) {
queue<Node*> q;
q.push(root);
int num = 0;
while (!q.empty()) {
Node* top = q.front();
q.pop();
if (num != 0) printf(" ");
num++;
printf("%d", top->data);
if (top->lchild != NULL) q.push(top->lchild);
if (top->rchild != NULL) q.push(top->rchild);
}
}
- 中序遍历的堆栈方法,入栈顺序时先序,出栈顺序是中序
- 树的遍历
先跟遍历(dfs) 层序遍历(bfs)
struct Node {
typename data;
vector child;
}node[maxn];
int index = 0;
int newNode(int v) {
node[index].data = v;
node[index].child.clear();
return index++;
}
//层序遍历
void bfs(int root) {
queue<int> q;
q.push(root);
node[root].layer = 0;
while (!q.empty()) {
int top = q.front();
q.pop();
if (node[top].child.size() == 0) ans += p * pow(1 + r, node[top].layer)*node[top].amount;
for (int i = 0; i < node[top].child.size(); ++i) {
int child = node[top].child[i];
node[child].layer = node[top].layer + 1;
q.push(child);
}
}
}
//先跟遍历
void dfs(int root, int depth){
if(node[root].child.size() == 0){
ans+=node[root].data*pow(1+r,depth);
return;//递归边界隐含在其中
}
for(int i=0;i<node[root].child.size();++i){
dfs(node[root].child[i],depth+1)
}
}
- BST模板
struct Node {
int data;
Node* l; Node* r;
};//生成指针节点时要记得将左右孩子赋值为NULL;
void insert(Node* &root, int data) {
if (root == NULL) {
root = new Node;//需要再分配空间 但是不需要再重新定义
root->data = data;
root->l = root->r = NULL;
return;//递归边界
}
else if (root->data > data) insert(root->l, data);
else insert(root->r, data);
}
void inorder(int root) {//因为完全**BST中序遍历是升序**,现在用数组确定了树的位置只需要**中序遍历填值**就行 2*root>n表示叶子节点
if (root > n) return;
inorder(2 * root);
node[root] = num[++index];
inorder(2 * root + 1);
}
- AVL
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
struct Node {
int key, height;
Node* l;
Node* r;
}*root;
int n;
Node* newnode(int v) {
Node* node = new Node;
node->key = v;
node->height = 1;
node->l = node->r = NULL;
return node;
}
int getheight(Node* root) {
if (root == NULL) return 0;
return root->height;
}
void upheight(Node* &root) {
root->height = max(getheight(root->l), getheight(root->r)) + 1;
}
int getb(Node* root) {
return getheight(root->l) - getheight(root->r);
}
void r(Node* &root) {
Node* temp = root->l;
root->l = temp->r;
temp->r = root;
upheight(root);
upheight(temp);
root = temp;
}
void l(Node* &root) {
Node* temp = root->r;
root->r = temp->l;
temp->l = root;
upheight(root);
upheight(temp);
root = temp;
}
void insert(Node* &root, int v) {
if (root == NULL) {
root = newnode(v);
return;
}
if (v < root->key) {
insert(root->l, v);
upheight(root);
if (getb(root) == 2) {
if (getb(root->l) == 1) {//ll
r(root);
}
else if (getb(root->l) == -1) {
l(root->l);//**一定是左孩子的左孩子**
r(root);//lr
}
}
}
else {
insert(root->r, v);
upheight(root);
if (getb(root) == -2) {
if (getb(root->r) == -1) {//rr
l(root);
}
else if (getb(root->r) == 1) {
r(root->r);**//一定是右孩子的有孩子**
l(root);//rl
}
}
}
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
int key; scanf("%d", &key);
insert(root, key);
}
printf("%d\n", root->key);
return 0;
}
- 并查集
int n;
int father[maxn];
int isrooot[maxn];//是根的数目,即该集合的人数
int course[maxn];//第一个有这个嗜好的人
int findfather(int x) {
int a = x;
while(x!=father[x]){//找到根
x = father[x];
}
while (a != father[a]) {
//回溯到尽头
father[a] = x;
a = father[a];
}
return x;
}
void U(int a, int b) {
int fa = findfather(a);
int fb = findfather(b);
if (fa != fb) {
father[fa] = fb;
}
}
void init() {
for (int i = 1; i <= n; ++i) {
father[i] = i;
isrooot[i] = false;
}
}
- 堆排序
void downjust(int l, int h) {
int i = l, j = 2 * i;
while (j <= h) {
if (j + 1 <= h && heap[j + 1] > heap[j]) j = j + 1;//heap从1开始存
if (heap[j] > heap[i]) {
swap(heap[j], heap[i]);
i = j;
j = 2 * i;
}
else break;
}
}
void create() {
for (int i = n / 2; i >= 1; --i) {
downjust(i, n);
}
}
void heapsort() {
create();
for (int i = n; i > 1; --i) {
if (i != n && heap == changed) {
swap(heap[1], heap[i]);
downjust(1, i - 1);
printf("Heap Sort\n");
return;
}
swap(heap[1], heap[i]);
downjust(1, i - 1);
}
}
void deletetop() {
heap[1] = heap[n--];
downjust(1, n);
}
void upjust(int l, int h) {
int i = h, j = i / 2;
while (j >= l) {
if (heap[j] > heap[i]) {
swap(heap[i], heap[j]);
i = j, j = i / 2;
}
else break;
}
}
void insert(int x) {
heap[++n] = x;
upjust(1, n);
}
七、图
一般由防止重复访问点和防止走回头路的情况,前者一般用bool vis[],后者一般用前驱
- dfs
const int maxn = 1010;
int G[maxn][maxn] = { 0 };//无路
bool vis[maxn] = { false };//是否访问
void dfs(int u) {
vis[u] = true;
for (int v = 1; v <= n; ++v) {
if (G[u][v] == 1 && vis[v] == false) {
dfs(v);
}
}
}
void dfstra(int& num) {
for (int v = 1; v <= n; ++v) {
if (vis[v] == false) {
dfs(v);
num++;
}
}
}
//计算连通块数、dfs遍历图及存储路径或树的叶节点、set的运用——赋值、遍历;
int n,maxdepth = -1;
const int maxn = 10010;
vector<int> G[maxn];
set<int> temp,ans;//自动去重排序
int father[maxn], pre[maxn];//并查集用来算连通块数 pre用来避免走回头路
bool isroot[maxn] = { 0 };
int findfather(int x) {
while (x != father[x]) {
x = father[x];
}
int a = x;
while (father[a] != x) {
father[a] = x;
a = father[a];
}
return x;
}
void U(int a, int b) {
int fa = findfather(a);
int fb = findfather(b);
if (fa != fb) {
father[fa] = findfather(fb);
}
}
void init() {
for (int i = 1; i <= n; ++i) father[i] = i;
}
int calblock() {
int block = 0;
for (int i = 1; i <= n; ++i) {
int root = findfather(i);
isroot[root] = 1;
}
for (int i = 1; i <= n; ++i) block += isroot[i];
return block;
}
void dfs(int u, int depth, int p) {
if (depth > maxdepth) {
temp.clear();//不到叶子节点会一直清空之前的节点
temp.insert(u);
maxdepth = depth;
}
else if (depth == maxdepth) {
temp.insert(u);
}
for (int i = 0; i < G[u].size(); ++i) {
if (G[u][i] == p) continue;//防止走回头路 这与vis用法不一样,vis是只要访问过的点就不在访问,pre前驱是不访问同一条边 一般是和生成树的高度有关
dfs(G[u][i], depth + 1, u);
}
}
int main() {
scanf("%d", &n);
init();
for (int i = 1; i < n; ++i) {
int u, v; scanf("%d %d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
U(u, v);
}
int Block = calblock();
if (Block != 1) printf("Error: %d components", Block);
else {
dfs(1, 0, -1);
ans = temp;
temp.clear();
dfs(*ans.begin(), 0, -1);
for (auto it : temp) {
ans.insert(it);
}
for (auto it : ans) {
cout << it << endl;
}
}
return 0;
}
- 但问题涉及到不超过多少层计数时往往用BFS更方便
const int maxn = 1010;
const int inf = 10000010;
int n, l;
bool inq[maxn] = { 0 };
vector<int> g[maxn];
struct Node {
vector<int> adj;
int l;
}node[maxn];
void bfs(int s,int& num) {
queue<int> q;
q.push(s);
inq[s] = true;
node[s].l = 0;
while (!q.empty()) {
int top = q.front();
q.pop();
if (node[top].l > 0 && node[top].l <= l) num++;
for (int i = 0; i < node[top].adj.size(); ++i) {
int next = node[top].adj[i];
if (inq[next] == false) {
node[next].l = node[top].l + 1;
q.push(next); inq[next] = true;
}
}
}
}
int main() {
scanf("%d %d", &n, &l);
for (int i = 1; i <= n; ++i) {
int k; scanf("%d", &k);
while (k--) {
int parent; scanf("%d", &parent);
g[parent].push_back(i);
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < g[i].size(); ++j) {
node[i].adj.push_back(g[i][j]);
}
}
int query; scanf("%d", &query);
while (query--) {
int root; scanf("%d", &root);
int num = 0;
bfs(root, num);
printf("%d\n", num);
memset(inq, false, sizeof(inq));
}
return 0;
}
- 最短路径(主要时dijkstra)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
using namespace std;
const int maxn = 510;
const int inf = 1000000006;
int g[maxn][maxn], weight[maxn], w[maxn], d[maxn],n,m,st,ed,num[maxn];
bool vis[maxn] = { 0 };
void dijkstra(int s) {
fill(d, d + maxn, inf);
fill(w, w + maxn, 0);//有疑问 这里时求最大的w
memset(vis, false, sizeof(vis));
memset(num, 0, sizeof(num));//用于记录到某点最短路径长度
num[s] = 1;
d[s] = 0; w[s] = weight[s];//s->s的点权和是本身的点权
for (int i = 0; i < n; ++i) {
int Min = inf, u = -1;
for (int j = 0; j < n; ++j) {
if (vis[j] == false && d[j] < Min) {
u = j;
Min = d[j];
}
}
if (u == -1) return;
vis[u] = true;
for (int v = 0; v < n; ++v) {
if (g[u][v] != inf && vis[v] == false) {
if (d[u] + g[u][v] < d[v]) {
d[v] = d[u] + g[u][v];
w[v] = w[u] + weight[v];
num[v] = num[u];//用于记录到某点路径长度
}
else if (d[u] + g[u][v] == d[v]) {
num[v] = num[u] + num[v];//画图理解清除就行,**最短路径数**最后第一个更新的地方
if (w[u] + weight[v] > w[v]) {
w[v] = w[u] + weight[v];
}
}
}
}
}
}
int main() {
fill(g[0], g[0] + maxn * maxn, inf);
scanf("%d %d %d %d", &n, &m, &st, &ed);
for (int i = 0; i < n; ++i) scanf("%d", &weight[i]);
while (m--) {
int c1, c2,l;
scanf("%d %d %d", &c1, &c2,&l);
g[c1][c2] = g[c2][c1] = l;
}
dijkstra(st);
printf("%d %d\n", num[ed], w[ed]);
return 0;
}
//dijkstr + dfs 此模板非常重要可以大幅降低思维难度
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
using namespace std;
const int maxn = 510;
const int inf = 1000000006;
int g[maxn][maxn], weight[maxn], d[maxn],sp,n,m,cmax;
bool vis[maxn] = { 0 };
vector<int> pre[maxn],temp,path;
void dijkstr(int s) {
fill(d, d + maxn, inf);
d[s] = 0;
memset(vis, false, sizeof(vis));
for (int i = 0; i <= n; ++i) {
int MIN = inf, u = -1;
for (int j = 0; j <= n; ++j) {
if (vis[j] == false && d[j] < MIN) {
MIN = d[j]; u = j;
}
}
if (u == -1) return;
vis[u] = true;
for (int v = 0; v <= n; ++v) {
if (g[u][v] != inf && vis[v] == false) {
if (d[v] > d[u] + g[u][v]) {
d[v] = d[u] + g[u][v];
pre[v].clear();
pre[v].push_back(u);
}
else if (d[v] == d[u] + g[u][v]) pre[v].push_back(u);
}
}
}
}
int mintime = inf,minbacknum = inf,minneed=inf;
void dfs(int s) {
if (s == 0) {
temp.push_back(s);//最后一点--起点id
int need = 0, remain = 0;
for (int i = temp.size()-1; i >= 0; --i) {
int id = temp[i];
if (weight[id] > 0) remain += weight[id];//点权为负说明少了
else {
if (remain > abs(weight[id])) remain -= abs(weight[id]);
else {
need += abs(weight[id]) - remain;
remain = 0;
}
}
}
if (need < minneed) {
minneed = need;
path = temp;
minbacknum = remain;
}
else if (need == minneed) {
if (remain < minbacknum) {
path = temp;
minbacknum = remain;
}
}
temp.pop_back();
return;
}
temp.push_back(s);
for (int i = 0; i < pre[s].size(); ++i) {
dfs(pre[s][i]);
}
temp.pop_back();//一定要在循环外面
}
int main() {
fill(g[0], g[0] + maxn * maxn, inf);
scanf("%d %d %d %d", &cmax, &n, &sp, &m);
weight[0] = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d", &weight[i]);
weight[i] -= cmax/2 ;
}
while (m--) {
int s1, s2, t;
scanf("%d %d %d", &s1, &s2, &t);
g[s1][s2] = g[s2][s1] = t;
}
dijkstr(0);
dfs(sp);
printf("%d ", minneed);
for (int i = path.size() - 1; i >= 0; --i) {
printf("%d", path[i]);
if (i != 0) printf("->");
}
printf(" %d\n", minbacknum);
return 0;
}
七、 动态规划
1,01背包问题
2,完全背包
每件物品无限个