1.树状数组板子
https://blog.csdn.net/henulmh/article/details/98596306
2.0x3f3f3f3f与0x7fffffff
https://blog.csdn.net/JuJuBang/article/details/105603867
MAXINT 0x7fffffff
MININT 0x80000000
3.最短路
堆优化dij的链式前向星实现
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<string.h>
using namespace std;
const int N = 1e5 + 3;
struct edge { int w, to, next; };
struct node {
int dis, num;
bool operator< (const node &x) const {
return dis>x.dis;
}
};
priority_queue<node> que;
int dis[N];
int head[N];
edge E[2*N];
int vis[N];
int cnt;
int n, m, s;
void addedge(int u,int v,int w) {
cnt++;
E[cnt].to = v;
E[cnt].w = w;
E[cnt].next = head[u];
head[u] = cnt;
}
void dij() {
dis[s] = 0;
que.push(node{ 0,s });
while (que.empty() == false) {
node temp;
temp = que.top();
que.pop();
int num = temp.num;
if (vis[num]) continue;
else vis[num] = 1;
for (int i = head[num]; i; i = E[i].next) {
int tot = E[i].to;
if (dis[tot] > dis[num] + E[i].w){
dis[tot] = dis[num] + E[i].w;
if (!vis[tot]) que.push(node{ dis[tot],tot });
}
}
}
}
int main() {
cin >> n >> m >> s;
memset(dis, 0x3f, sizeof(dis));
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
addedge(u, v, w);
}
dij();
for (int i = 1; i <= n; i++) {
cout << dis[i] << " ";
}
return 0;
}
bf算法
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<string.h>
using namespace std;
const int N = 1e4 + 5;
const int M = 5e5 + 5;
int n, m, s;
int u[N];
int v[N];
int w[N];
int dis[N];
int main() {
cin >> n >> m >> s;
memset(dis, 0x3f, sizeof(dis));
for (int i = 1; i <= m; i++) {
cin >> u[i] >> v[i] >> w[i];
}
dis[s] = 0;
int flag = 1;
for (int i = 1; i <= n - 1; i++) {
flag = 1;
for (int j = 1; j <= m; j++) {
if (dis[u[j]] + w[j] < dis[v[j]]) {
dis[v[j]] = dis[u[j]] + w[j];
flag = 0;
}
}
if (flag == 1) break;//松弛完毕提前退出
}
for (int i = 1; i <= n; i++) {
cout << dis[i] << " ";
}
}
SPFA算法
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<string.h>
using namespace std;
const int N = 1e4 + 5;
const int M = 5e5 + 5;
int n, m, s;
int dis[N];
int inq[N];
struct edge {
int to, next, w;
};
int cnt;
edge E[M];
int head[N];
void add_edge(int u, int v, int w) {
cnt++;
E[cnt].to = v;
E[cnt].w = w;
E[cnt].next = head[u];
head[u] = cnt;
}
void spfa() {
memset(dis, 0x3f, sizeof(dis));
queue<int> que;
que.push(s);
dis[s] = 0;
inq[s] = 1;
while (que.empty() == false) {
int t = que.front();
que.pop();
inq[t] = 0;
for (int j = head[t]; j; j = E[j].next) {
int tot = E[j].to;
if (dis[tot] > dis[t] + E[j].w) {
dis[tot] = dis[t] + E[j].w;
if (!inq[tot]) {//注意这里只有进行松弛的边才有机会加入queue
que.push(tot);
inq[tot] = 1;
}
}
}
}
}
int main() {
cin >> n >> m >> s;
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
add_edge(u, v, w);
}
spfa();
for (int i = 1; i <= n; i++) {
if (dis[i] == 0x3f3f3f3f) cout << 0x7fffffff << " ";
else cout << dis[i] << " ";
}
}
4.判断负环
贝尔曼·福特
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<string.h>
using namespace std;
struct Edge {
int u = 0, v = 0, w = 0;
};
Edge E[6005];
int dis[2005];
int add(int a, int b) {∞+x=∞,所以要手写加法函数判断这种情况
if (a == 0x3f3f3f3f || b == 0x3f3f3f3f)
return 0x3f3f3f3f;
else
return a + b;
}
int main() {
int t;
cin >> t;
while (t--) {
memset(E, 0, sizeof(E));
memset(dis, 0x3f, sizeof(dis));
int n, m;
cin >> n >> m;
dis[1] = 0;
int cnt = 1;
for (int i = 1; i <= m; i++) {
cin >> E[cnt].u >> E[cnt].v >> E[cnt].w;
if (E[cnt].w >= 0) {
E[cnt + 1].v = E[cnt].u;
E[cnt + 1].u = E[cnt].v;
E[cnt + 1].w = E[cnt].w;
cnt++;
}
cnt++;
}
for (int i = 1; i < n; i++) {
int flag = true;
for (int j = 1; j < cnt; j++) {
if (add(dis[E[j].u], E[j].w) < dis[E[j].v]) {
dis[E[j].v] = dis[E[j].u] + E[j].w, flag = false;
}
}
if (flag == true) break;
}
int f1 = 0;
for (int j = 1; j < cnt; j++) {
if (add(dis[E[j].u], E[j].w) < dis[E[j].v]) {
cout << "YES" << endl; f1 = 1; break;
}
}
if (f1 == 0) cout << "NO" << endl;
}
}
spfa
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<string>
#include<queue>
using namespace std;
const int M = 6e3 + 5;
const int N = 2e3 + 5;
int cnt = 0;
int dis[N];
int inq[N];
int coun[N];
int head[N];
int n, m;
struct E { int to, next, w; };
E edge[M];
void add_edge(int u, int v, int w) {
cnt++;
edge[cnt].to = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
int add(int a, int b) {
if (a == 0x3f3f3f3f || b == 0x3f3f3f3f)
return 0x3f3f3f3f;
else return a + b;
}
void init() {
cnt = 0;
memset(dis, 0x3f, sizeof(dis));
memset(inq, 0, sizeof(inq));
memset(coun, 0, sizeof(coun));
memset(head, 0, sizeof(head));
}
bool spfa() {
queue<int> que;
que.push(1);
inq[1] = 1;
dis[1] = 0;
while (!que.empty()) {
int t = que.front();
que.pop();
inq[t] = 0;
for (int j = head[t]; j; j = edge[j].next) {
int tot = edge[j].to;
if (dis[tot] >add( dis[t] , edge[j].w)) {
dis[tot] = add(dis[t], edge[j].w);
coun[tot]++;
if (coun[tot] >= n) return true;//存在负环
if (!inq[tot]) {
inq[tot] = 1;
que.push(tot);
}
}
}
}
return false;
}
int main() {
int t;
cin >> t;
while (t--) {
cin >> n >> m;
init();
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
add_edge(u, v, w);
if (w > 0) add_edge(v, u, w);
}
if (spfa()) cout << "YES" << endl;
else cout << "NO" << endl;
}
}
5.最小生成树
递归并查集+kruskal
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int M = 2e5 + 4;
const int N = 5e3 + 4;
struct edge {
int u, v, w;
};
edge E[M];
int f[N];
bool cmp(edge a, edge b) {
return a.w < b.w;
}
int father(int u) {//返回u的祖宗
if (u == f[u]) return u;
else {
f[u] = father(f[u]);//路径压缩,没有找到祖宗就继续找父亲的祖宗
return f[u];
}
}
int main() {
int ans = 0;
int cnt = 0;
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> E[i].u >> E[i].v >> E[i].w;
}
for (int i = 1; i <= n; i++) {
f[i] = i;
}
sort(E + 1, E + 1 + m, cmp);
for (int i = 1; i <= m; i++) {
int u = father(E[i].u);
int v = father(E[i].v);
if (u==v) continue;
else {
f[u] = v;//注意这里是求父亲以后再进行赋值的!!!
cnt++;//加入一条边
ans += E[i].w;
}
if (cnt == n - 1) {//加入n-1条边,n个顶点最少要n-1条边才能连接在一起
cout << ans << endl;
return 0;
}
}
if (cnt < n - 1) {
cout << "orz" << endl;
return 0;
}
}
prim+链式前向星
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<string>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
int n, m;
const int N = 5e3 + 4;
const int M = 2e5+4 ;
struct edge { int to, next, w; };
int cnt;
int head[N];
edge e[2*M];
void add_edge(int u, int v, int w) {
cnt++;
e[cnt].w = w;
e[cnt].next = head[u];
e[cnt].to = v;
head[u] = cnt;
}
int dis[N];
int vis[N];
int prim() {
int ans = 0;
//prim算法是合并顶点,所以任选一个为起点即可
memset(dis, 0x3f, sizeof(dis));
dis[1] = 0;
vis[1] = 1;
int num = 1;//生成树中的顶点数目
for (int i = head[1]; i; i = e[i].next) {
int tot = e[i].to;
dis[tot] = min(dis[tot],e[i].w) ;//特别注意,合并边的时候可能会出现重边,所以这里要用min!!!prim是对边进行合并的!
}
while (num < n) {
int min = 0x3f3f3f3f;
int u = -1;
for (int i = 1; i <= n; i++) {
if (!vis[i] && dis[i] < min) {
min = dis[i];//每次选择与生成树距离最小的顶点新加入生成树
u = i;
}
}
if (u == -1) break;//无法继续加入顶点了
vis[u] = 1;
ans += min;
num++;
for (int i = head[u]; i; i = e[i].next) {
int tot = e[i].to;
if (!vis[tot] && dis[tot] > e[i].w) {
dis[tot] = e[i].w;//更新各点与最小生成树之间的距离,这里是直接更新为两点之间的边权
}
}
}
if (num == n ) return ans;
else return -1;//图不联通
}
int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
add_edge(u, v, w);
add_edge(v, u, w);
}
int ans=prim();
if (ans == -1) {
cout << "orz" << endl;
}
else
cout << ans << endl;
}
6.并查集求图中存在的最小环
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int N=2e5+3;
int fa[N],a[N];
int getf(int x,int &cnt){
cnt++;
if(x==fa[x]) return x;
else{
return getf(fa[x],cnt);//由于需要求最小环的长度,所以不能进行路径压缩
}
}
int ans=0x3f3f3f3f;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
fa[i]=i;
}
for(int i=1;i<=n;i++){
cin>>a[i];
int cnt=0;
if(getf(a[i],cnt)==i){
ans=min(ans,cnt);
}
else{
fa[i]=a[i];
}
}
cout<<ans<<endl;
}
**7.线性筛/欧拉筛
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<string>
#include<queue>
using namespace std;
typedef long long ll;
int f[1000005];
int prime[1000005];
int keep[1000005];
int ans = 0;
int shai(int x) {
int cnt = 0;
for (int i = 2; i <= x; i++) {
if (f[i] == 0) {
prime[cnt++] = i;
}
keep[i] = cnt;
for (int j = 0; j < cnt; j++) {
if (prime[j] * i > x) break;
f[i*prime[j]] = 1;
if (i%prime[j] == 0) break;
}
}
}
int main() {
int n, m;
cin >> n >> m;
shai(m);
while (n--) {
int l, r;
cin >> l >> r;
if (!(l >= 1 && r <= m)) {
printf("Crossing the line\n");
continue;
}
else {
printf("%d\n", keep[r] - keep[l - 1]);
}
}
}
8.kmp
#include<iostream>
#include<stdio.h>
#include<vector>
#include<string>
#include<queue>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
void prefix_table(int prefix[], string pattern) {//求next数组
prefix[0] = 0;
int len = 0;
int n = pattern.length();
int i = 1;
while (i < n) {
if (pattern[i] == pattern[len]) {
len++;
prefix[i] = len;
i++;
}
else {
if (len > 0) {
len = prefix[len - 1];
}
else {
prefix[i] = len;
i++;
}
}
}
}
void move_prefix(int prefix[], int n) {//移动前缀数组
for (int i = n - 1; i > 0; i--) {
prefix[i] = prefix[i - 1];
}
prefix[0] = -1;
}
void kmp_search( string text, string pattern) {//kmp搜索
int n = text.length();
int m = pattern.length();
int i = 0, j = 0;
int *prefix=(int*)malloc(sizeof(int)*(m));//动态分配数组内存
int *prefix_keep = (int*)malloc(sizeof(int)*(m));
prefix_table(prefix, pattern);
for (int i = 0; i < m; i++) {
prefix_keep[i] = prefix[i];
}
move_prefix(prefix, m);
while (i < n) {
if (j == m - 1 && text[i] == pattern[j]) {
printf("%d\n", i+1-j);
j = prefix[j];
}
if (text[i] == pattern[j]) {
i++;
j++;
}
else {
j = prefix[j];
if(j==-1)
{
i++;
j++;//j==0,从头开始匹配
}
}
}
for (int i = 0; i < m; i++) {
cout << prefix_keep[i] << " ";
}
}
9.刷题集锦
(1)最大正方形 leetcodeP221:二维dp
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
int dp[1000][1000]={0};
int ans=0;
for(int i=0;i<matrix.size();i++){
for(int j=0;j<matrix[0].size();j++){
if(matrix[i][j]=='1')
dp[i][j]=1;
ans=max(ans,dp[i][j]);
}
}
for(int i=1;i<matrix.size();i++){
for(int j=1;j<matrix[0].size();j++){
if(matrix[i][j]=='1')
dp[i][j]=min(dp[i-1][j-1],min(dp[i][j-1],dp[i-1][j]))+1;//(i,j为正方形右下角坐标)
ans=max(ans,dp[i][j]);
}
}
return ans*ans;
}
};
(2)树状dp+背包思想,通过链式前向星实现
luogu P2014
#include<iostream>
#include<stdio.h>
#include<vector>
#include<string>
#include<queue>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
//树上的选与不选的问题,树状dp
const int N = 300;
struct edge {
int next, to, w;
};
int m, n;
int cnt;
edge e[N];
int head[N];
void add(int u,int v,int w) {
cnt++;
e[cnt].to = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
}
int dp[N][N];//在以i为根结点的树中选j个的最大值
void dyp(int x) {//x为根节点时进行dp
for (int i = head[x]; i; i = e[i].next) {
int tot = e[i].to;
dyp(tot);//先对子树进行dp,全部子树结果获得了以后才能得到最终结果
for (int j = m+1; j >=1 ; j--) { //倒序,压缩空间,省略了对结点数目进行的循环dp[i][j][k],i个节点,前j个选k个
for (int k = 0; k <= j-1; k++) {
dp[x][j] = max(dp[x][j], dp[tot][k]+ dp[x][j- k]);//对所有的子树
}//从dp[x][i-1][j - k]转换过来的,选了父节点才可以选子节点
}
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int k, s;
cin >> k >> s;
add(k, i, s);//从k->i,i为儿子
dp[i][1] = s;
}
dyp(0);
cout << dp[0][m+1] << endl;//注意需要加上0结点,所以一共是m+1个
}
(3)分组dp,主件附件依赖luoguP1064
#include<bits/stdc++.h>
using namespace std;
struct item{
int v,p,q;
};
item I[61];
int fj[61][61];//主件携带的附件
int cnt[61];//记录附件的个数
int dp[32010];
int h[32010];
int main(){
int N,m;
cin>>N>>m;
for(int i=1;i<=m;i++){
cin>>I[i].v>>I[i].p>>I[i].q;//i 件物品的价格、重要度以及它对应的的主件
I[i].p*=I[i].v;
if(I[i].q){//使每件物品的价格与重要度的乘积的总和最大。
cnt[I[i].q]++;
fj[I[i].q][cnt[I[i].q]]=i;
}
}
for(int i=1;i<=m;i++){
if(I[i].q==0)//分组背包
{
for(int j=N;j>=I[i].v;j--)
h[j]=dp[j-I[i].v]+I[i].p;
for(int z=1;z<=cnt[i];z++){
for(int q=N;q>=I[fj[i][z]].v+I[i].v;q--)
h[q]=max(h[q],h[q-I[fj[i][z]].v]+I[fj[i][z]].p);
}
for(int j=N;j>=I[i].v;j--){
dp[j]=max(h[j],dp[j]);
}
}}
cout<<dp[N]<<endl;
}
leetcode 三数之和(双指针法实现)
class Solution {
public:
vector<vector> threeSum(vector& nums){
int n=nums.size();
sort(nums.begin(),nums.end());
set<vector<int> > se;
for(int i=0;i<n;i++){
if(i>0)
if(nums[i]==nums[i-1])
continue;
int begin=i+1;
int end=n-1;
while(begin<end){
if(nums[begin]+nums[i]+nums[end]==0){
se.insert({nums[begin],nums[i],nums[end]});
begin++;
end--;
}
else if(nums[begin]+nums[i]+nums[end]>0){
end--;
}
else if(nums[begin]+nums[i]+nums[end]<0){
begin++;
}
}
}
vector<vector<int> > answ(se.begin(),se.end());//使用set进行vector的初始化
return answ;
}
};
leetcode 1367. 二叉树中的列表//巧妙的dfs,两次递归过程
class Solution {
public:
bool dfs(ListNode* head, TreeNode* root) {
if(head==NULL )return true;
if(root==NULL) return false;
if(head->val!=root->val){
return false;
}
return dfs(head->next,root->left)||dfs(head->next,root->right);
}
bool isSubPath(ListNode* head, TreeNode* root) {
if(head!=NULL&&root==NULL) return false;
return dfs(head,root)||isSubPath(head,root->left)||isSubPath(head,root->right);
}
};