知识汇总1
1:最短路
链式前向星;
点击查看代码
int head[maxn],to[maxn],nxt[maxn],val[maxn],tot;
void add(int x,int y,int z)
{
to[++tot]=y;
val[tot]=z;
nxt[tot]=head[x];
head[x]=tot;
}
堆优化的dijkstra
点击查看代码
priority_queue<pair<int,int> >q;
void dijkstra(int s)
{
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
dis[s]=maxn;
q.push(make_pair(maxn,s));
while(q.size())
{
int x=q.top().second;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i],z=val[i];
if(dis[y]<min(z,dis[x]))
{
dis[y]=min(z,dis[x]);
q.push(make_pair(dis[y],y));
}
}
}
}
spfa
点击查看代码
void spfa(int s)
{
memset(dis,0x7f7f7f7f,sizeof(dis));
dis[s]=0;
queue<int>q;
q.push(s);
vis[s]=true;
while(q.size())
{
s=q.front();
q.pop();
vis[s]=false;
for(int i=head[s];i;i=nxt[i])
{
int y=to[i];
if(dis[y]>dis[s]+val[i])
{
dis[y]=dis[s]+val[i];
if(!vis[y])
{
cnt[y]++;//判断负环,如果次数大于等于n,则有负环
q.push(y);
vis[y]=true;
}
}
}
}
}
floyd
注意,k的循环一定要放最外层,dis[i][j]的本质是经过前k个点所取得的最短路,每一次增加一个k,是用这个k去更新现有全部路径,实际上它的本质是dp,由点带面,与下文dp一样。
点击查看代码
void floyd()
{
for(int k=1;i<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
}
2:搜索
模板
点击查看代码
int Search(int k)
{
if (到目的地) 输出解;
else
for (i=1;i<=算符种数;i++)
if (满足条件)
{
保存结果;
Search(k+1);
恢复:保存结果之前的状态{回溯一步}
}
}
3树
普通树转二叉树
点击查看代码
//结构图存图
struct tree
{
char data;
int prt,lch,rch;
};tree a[10000];
int main()
{
cc();
cout<<endl;
return 0;
}
void cc()
{
memset(a,0,sizeof(a));
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].data;
int j,p;
cin>>j;
//第一个儿子变左儿子
if(j!=0)
{
a[i].lch=j;
a[j].prt=i;
}
//其余儿子为右儿子
p=j;
while(j!=0)
{
cin>>j;
if(j!=0)
{
a[p].rch=j;
a[j].prt=p;
p=j;
}
}
}
}
二叉树的遍历
点击查看代码
void qian(int x)
{
if(x!=0)
{
cout<<a[x].data;
qian(a[x].lch);
qian(a[x].rch);
}
}
void zhong(int x)
{
if(x!=0)
{
zhong(a[x].lch);
cout<<a[x].data;
zhong(a[x].rch);
}
}
void hou(int x)
{
if(x!=0)
{
hou(a[x].lch);
hou(a[x].rch);
cout<<a[x].data;
}
}
最优二叉树(哈夫曼树)
叶子之和为其根,n个叶子共组成2n-1的树
点击查看代码
#include<bits/stdc++.h>
using namespace std;
struct tree//结构体存图
{
long long data;
long long lch,rch;
};
long long n,k;
tree a[200],b[200];
void init();
long long my_min();
long long my_max();
void min_huffman();
void max_huffman();
int main()
{
init();
max_huffman();
min_huffman();
cout<<a[2*n-1].data<<endl;
cout<<b[2*n-1].data<<endl;
return 0;
}
void init()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].data;
b[i].data=a[i].data;
}
}
long long my_min()//最小叶子节点
{
long long temp=0x7fffffffffffffff;
for(int i=1;i<=n;i++)
{
if(a[i].data<temp)
{
temp=a[i].data;
k=i;
}
}
a[k].data=0x7fffffffffffffff;
return temp;
}
long long my_max()//最大叶子节点
{
long long temp=-1;
for(int i=1;i<=n;i++)
{
if(b[i].data>temp)
{
temp=b[i].data;
k=i;
}
}
b[k].data=-1;
return temp;
}
void min_huffman()
{
for(int i=n+1;i<=2*n-1;i++)
{
a[i].lch=my_min();
a[i].rch=my_min();
a[i].data=a[i].lch*a[i].rch+1;
a[k].data=a[i].data;
}
}
void max_huffman()
{
for(int i=n+1;i<=2*n-1;i++)
{
b[i].lch=my_max();
b[i].rch=my_max();
b[i].data=b[i].lch*b[i].rch+1;
b[k].data=b[i].data;
}
}
dp
个人理解:dp本质为暴力枚举 + 记忆化优化,以小边界带动整体,以点带面的逐级求值过程
个人dp方程理解:1:中间阶段转移情况联系实际 。2,点带面边界处理
背包板子
t[ ]:体积,v[ ]:价值
01背包
点击查看代码
for(int i=1;i<=m;i++)
for(int j=n;j>=t[i];j--)//一定要倒序,保证dis[j-t[i]]在本次循环未更新
{
dis[j]=max(dis[j],dis[j-t[i]]+v[i]);
}
完全背包
点击查看代码
for(int i=1;i<=m;i++)
for(int j=t[i];j<=n;j++)//这里为正序,因为物品有无数件
{
dis[j]=max(dis[j],dis[j-t[i]]+v[i]);
}
分组背包
点击查看代码
for(int i=1;i<=n;i++){
int x,y,p;
scanf("%d%d%d",&x,&y,&p);
q[p]++; //p组数量++
t[p][q[p]]=x;
v[p][q[p]]=y; //转换01背包
}
for(int i=1;i<=m;i++)
for(int j=v;j>=1;j--)
{
dis[i][j]=dis[i-1][j];
for(int s=1;s<=q[i];s++)
{
if(j>=t[i][s])
dis[i][j]=max(dis[i][j],dis[i-1][j-t[i][s]]+v[i][s]);
}
}
背包变形
点击查看代码
//求叠加值种类/个数等问题,倒序遍历高度/重量等值,此时dis[i]表示i高度/重量可达到
for(int i=1;i<=n;i++)
for(int j=1;j<=p[i];j++)
for(int k=1000;k>=l[i];k--)
{
if(dis[k-l[i]])
{
dis[k]=1;
}
}
线性dp
b[ ]:长度,a[ ],序列
最长上升序列
点击查看代码
for(i=1;i<=n;i++)
{
b[i]=1;
for(j=1;j<=i;j++)//j是否等于i根据题目分析
{
if(a[i]>a[j] && b[j]+1>b[i])
{
b[i]=b[j]+1;
}
}
}
最长下降序列
点击查看代码
for(i=n;i>=1;i--)//倒着的最长上升序列即为最长下降序列
{
b[i]=1;
for(j=i+1;j<=n;j++)
{
if(a[i]>a[j] && b[j]+1>b[i])
{
b[i]=b[j]+1;
}
}
}
区间dp
枚举长度,起点,断点
板子
点击查看代码
for(int l=2;l<=n;l++)
for(int i=1;i+l-1<=m;i++)
{
int j=i+l-1;
for(int k=i;k<j;k++)
{
a[i][j]=min(a[i][j],a[i][k]+a[k+1][j]+具体数值);
}
}
环拆链
二倍长度枚举起点和长度;
点击查看代码
for(int i=1;i<=n;i++)
{
scanf("%d",&ch[i],&f[i][i]);
f[i+n][i+n]=f[i][i];
}
for(int l=2;l<=n;l++)
for (int i=1;i+l-1<=2*n;i++)
{
int j=i+l-1;
for(int k=i;k<j;k++)
{
f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+具体数值);
}
}
坐标dp
多维,看位置转移之间的关系,
i,j枚举位置。
例题:https://tg.hszxoj.com/contest/14/problem/5
(https://vijos.org/p/1006)
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,x,ans,a[1000][1000],f[1000][1000],tem;
main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
scanf("%lld",&a[i][j]);
}
}
f[1][0]=a[1][0];
for(int i=2;i<=n;i++){
for(int j=0;j<i;j++){
f[i][j]=min(f[i-1][(j-2+i)%(i-1)],f[i-1][j%(i-1)])+a[i][j];
}
for(int j=0;j<i;j++){
f[i][j]=min(f[i][j],f[i][(j-1+i)%i]+a[i][j]);
}
for(int j=i-1;j>=0;j--){
f[i][j]=min(f[i][j],f[i][(j+1)%i]+a[i][j]);
}
for(int j=0;j<i;j++){
f[i][j]=min(f[i][j],f[i][(j-1+i)%i]+a[i][j]);
}
for(int j=i-1;j>=0;j--){
f[i][j]=min(f[i][j],f[i][(j+1)%i]+a[i][j]);
}
}
printf("%lld",f[n][0]);
return 0;
}
i,j枚举横坐标
例题:https://www.acwing.com/problem/content/description/277/
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,x,ans,a[100][100],dp[150][101][101],tem;
main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
scanf("%lld",&a[i][j]);
}
for(int l=1;l<=m+n;l++)
for(int i1=1;i1<l;i1++)
for(int i2=1;i2<l;i2++){
tem=a[i1][l-i1];
if(i1!=i2) tem+=a[i2][l-i2];
int &v=dp[l][i1][i2];
v=max(v,dp[l-1][i1-1][i2]);
v=max(v,dp[l-1][i1][i2-1]);
v=max(v,dp[l-1][i1][i2]);
v=max(v,dp[l-1][i1-1][i2-1]);
v+=tem;
}
printf("%lld",dp[n+m][n][n]);
return 0;
}
树形dp
dfs + dp 没啥可说的
主要思路是按存树方式存图再遍历,dfs中根据儿子,父亲,根的关系dp(多数情况是由叶子信息推根)