上了半个暑假的课,马上结束了
老师叫我们写个总结
1.最小生成树
Prim,Kruskal,动态,次小
好像还要自学一下次小生成树
Prim--类似dijkstra,按最短路增广,堆优化
Kruskal--边权排序,并查集判环,加n-1条边即可
动态最小--Kruskal+插入排序 O(n^2)
//Prim
//dis为连到最小生成树的最短距离
memset(dis,-1,sizeof(dis));
dis[1]=0,vis[1]=1;
priority_queue<pair<int,int> >q;
q.push(make_pair(0,1));//dis,pos
while(!q.empty()){
int Dis=q.top().first,u=q.top().second;
q.pop();
if(vis[u])continue;
vis[u]=1;
ans+=Dis;
for(i=head[u];i;i=next[i]){
int v=to[i];
if(!vis[v]&&(dist[v]>val[i]||dist[v]==-1)){
dist[v]=val[i];
q.push(make_pair(dist[v],v));
}
}
}
}
//Kruskal
memset(F,-1,sizeof(F));
sort(edge,edge+tol,cmp);
int cnt=0;//计算加入的边数
int ans=0;
for(int i=0;i<tol;i++)
{
int u=edge[i].u,v=edge[i].v,w=edge[i].w;
u=Find(u),v=Find(v);
if(u!=v)
{
ans+=w;
F[u]=v;
cnt++;
}
if(cnt==n-1)break;
}
动态最小生成树
#include<bits/stdc++.h>
#define N 10005
using namespace std;
int n,h,fa[N],pd,Max,sum;
struct Node{int x;int y;int w;}a[N];
int getfather(int x){//并查集
if(fa[x]==x) return x;
fa[x]=getfather(fa[x]);
return fa[x];
}
void Sort(Node cur)//插入排序
{
int i,j;
for(i=sum;i>=1;i--) if(a[i].w<=cur.w) break;
for(j=sum;j>=i+1;j--) a[j+1]=a[j];
a[i+1]=cur,sum++;
}
int Kruskal()
{
int ans=0,k=0;
for(int i=1;i<=sum;i++) fa[i]=i;
for(int i=1;i<=sum;i++){
int x=getfather(a[i].x),y=getfather(a[i].y);
if(x!=y) fa[x]=y,ans+=a[i].w,Max=a[i].w,k++;
if(k==n-1) break;
}
if(k<n-1) return 0;
return ans;
}
int main()
{
//freopen("1.in","r",stdin);
cin>>n>>h;
for(int i=1;i<=h;i++){
Node cur;
int ans;
cin>>cur.x>>cur.y>>cur.w;
if(pd&&cur.w>Max){cout<<ans<<endl;continue;}//如果插入的边
//比当前最小生成树的最大边还大,则不更新
Sort(cur);
if(ans=Kruskal()){cout<<ans<<endl;pd=1;}
else cout<<"-1"<<endl;
}
return 0;
}
次小生成树
int dis[N],used[N][N];//到最小生成树最短的距离
int vis[N],Map[N][N],Max[N][N];
//Map存边权,Max指i-j路径上的最大距离
int pre[N];//i的上一个点
int Prim()
{
for(int i=2;i<=n;i++) pre[i]=1,dis[i]=Map[1][i];
pre[1]=0,vis[1]=1,dis[1]=0;
for(int i=2;i<=n;i++){
int min_dis=inf,k;
for(int j=1;j<=n;j++){
if(!vis[j]&&min_dis>dis[j]){
min_dis=dis[j],k=j;
}
}
if(min_dis==inf) return -1;
vis[k]=1,ans+=min_dis;
used[k][pre[k]]=used[pre[k]][k]=1;
for(int j=1;j<=n;j++){
if (vis[j]&&used[j][k]==0) Max[j][k]=Max[k][j]=max(Max[j][pre[k]],dis[k]);
//计算两点之间若无连接,若连接上构成回路这条回路中最大的一条边,有动归的思想
if(!vis[j]&&dis[j]>dis[k]+Map[j][k]){
dis[j]=dis[k]+Map[j][k],pre[j]=k;
}
}
}
return ans;
}
int change(int min_ans)//最小生成树的权值和
{
int ans=inf;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
ans=min(ans,min_ans+Map[i][j]-Max[i][j]);
return ans;
}
我们枚举最小生成树外面的每一条边,减去环上的最大值;
求最小生成树的时候顺便求出最大值;
有点类似dp的思想
Max[j][k]=Max[k][j]=max(Max[j][pre[k]],dis[k]);
2.查分约束
求x3-x0的最大值
我们发现,最大值取决于这三个式在中最小的,即7
我们想到了最短路
求x0到x3的最短路
因为是求最大值,我们就要让最短路最大
即0->1->2->3的边权和最大
是多少呢?
我们看看,x1-x0<=2 想不想dis[0]+2>=dis[1],即0到1的距离<=2,最大为2
同理,1到2为3,2到3是2
0到3最短路的最大值为7
我们有此发现
对于x-y最大值问题,我们转成最短路,所有等式转为xi-xj<=k
然后add(j,i,k);
对于x-y最小值问题,我们转成最长路,所有等式转为xi-xj>=k
然后add(j,i,k);
但有时等式不统一,如何变形
xi-xj>=k => xj-xi<=k
xi-xj<k => xi-xj<=k+1
xi==xj => xi-xj<=0,xj-xi<=0
以此类推
//bfs
void spfa(int st)
{
int ans=0;
for(int i=1;i<=n;i++) dis[i]=0x3fffffff;
vis[st]=1,dis[st]=0;
queue<int> q;
q.push(st);
while(!q.empty()){
int x=q.front();
q.pop();
vis[x]=0;
if(++cnt[x]>n){ans=-1;break;}
for(int i=first[x];i;i=next[i]){
int t=to[i];
if(dis[t]>dis[x]+w[i]){
dis[t]=dis[x]+w[i];
if(vis[t]==0){
vis[t]=1;
q.push(t);
}
}
}
}
if(ans==-1) printf("%d\n",ans);
else if(dis[n]==0x3fffffff) printf("-2\n");
else printf("%d\n",dis[n]);
}
//dfs有负环的时候快一些
void spfa(int cur)
{
vis[cur]=true;
for(int i=first[cur];i;i=next[i]){
int t=to[i];
if(dis[t]>dis[cur]+w[i]){
dis[t]=dis[cur]+w[i];
if(vis[t]){flag=true;return;}
else spfa(t);
}
}
vis[cur]=false;
}
3.网络流,费用流
直接上代码吧
bool bfs()
{
queue<int> q;
for(int i=1;i<=n;i++) dis[i]=-1;
dis[st]=1,q.push(st);
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=first[x];i;i=next[i]){
int t=to[i];
if(w[i]<=0||dis[t]!=-1) continue;
dis[t]=dis[x]+1;
if(t==ed) return true;
q.push(t);
}
}
return false;
}
int dfs(int cur,int flow)
{
int ans=0;
if(cur==ed) return flow;
for(int &i=f2[cur];i;i=next[i]){
int t=to[i];
if(w[i]&&dis[t]==dis[cur]+1){
int delta=dfs(t,min(flow,w[i]));
w[i]-=delta,w[i^1]+=delta;
flow-=delta,ans+=delta;
if(flow==0) break;
}
}
if(flow) dis[cur]=-1;
return ans;
}
int dinic()
{
int ans=0;
if(bfs()){
memcpy(f2,first,sizeof(first));
if(int x=dfs(st,inf)) ans+=x;
}
return ans;
}
将图分层,最短路增广,tot注意开始赋成1
费用流
按最小费用增广,bfs时处理出dis[i]即到i的最小费用
bool bfs()
{
memset(dis,inf,sizeof(dis));
memset(vis,0,sizeof(vis));
queue<int> q;
dis[st]=1,q.push(st);
while(!q.empty()){
int x=q.front();
q.pop(),vis[x]=0;
for(int i=first[x];i;i=next[i]){
int t=to[i];
if(w[i]&&dis[t]>dis[x]+cost[i])
dis[t]=dis[x]+cost[i];
if(!vis[t])vis[t]=1,q.push(t);
}
}
return dis[ed]<inf;
}
int dfs(int cur,int flow)
{
int res=0;
if(cur==ed){ans+=flow*dis[ed];return flow;}
for(int &i=f2[cur];i;i=next[i]){
int t=to[i];
if(!vis2[t]&&w[i]&&dis[t]==dis[cur]+cost[i]){
//vis2避免负环,dis[t]==dis[cur]+cost[i]->最小费用增广
int delta=dfs(t,min(flow,w[i]));
w[i]-=delta,w[i^1]+=delta;
flow-=delta,res+=delta;
if(flow==0) break;
}
}
if(flow) dis[cur]=-1;
return res;
}
int dinic()
{
int ans=0;
while(bfs()){
memcpy(f2,first,sizeof(first));
dfs(st,inf);
}
return ans;
}
好像还有一种方法,费用流
4.数据结构 线段树,树状数组......
线段树不在多赘述
注意以下几个问题
//区间查询
if(l<=tree[root].l&&tree[root].r<=r) return tree[root].dat;
延迟标记
void spread(int x)
{
if(tree[x].tag){
tree[x*2].sum+=tree[x].tag*(tree[x*2].r-tree[x*2].l+1);
tree[x*2+1].sum+=tree[x].tag*(tree[x*2+1].r-tree[x*2+1].l+1);
tree[x*2].tag+=tree[x].tag;
tree[x*2+1].tag+=tree[x].tag;
tree[x].tag=0;
}
}
注意中间都是+=,每次都写成==......
遇到查询就下传,没有就不动。
树状数组
个人有几个不熟
void add(int pos,int val){
for(;pod<=n;pod+=lowbit(pos)) c[pos]+=val;
}
求逆序对
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int c[N],n,ans;
int read(){
int cnt=0;char ch=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
return cnt;
}
int lowbit(int x){return x&-x;}
int add(int pos,int val){
for(;pos<=n;pos+=lowbit(pos))
c[pos]+=val;
}
int getsum(int x){
int cnt=0;
for(;x;x-=lowbit(x))cnt+=c[x];
return cnt;
}
int main(){
n=read();
for(int i=1;i<=n;i++){
int a=read();
add(a,1);
ans+=i-getsum(a);
}
cout<<ans;
return 0;
}
区间修改,单点/区间查询,传送门
5.状压dp,树形dp
//炮兵阵地
/*
1.建state存 H ,状态&state[i]==0 就可以放
2.建st存所有状态,不用在后面枚举
3.st[i]&st[j]==1 就可以互相打
4.状态 -> f[i][st[i-1]][st[i]];
5.转移 -> f[i+1][st[i]][i+1行的所有可行状态]= f[i][st[i-1]][st[i]]+sum[st[i]];
6.最终答案 -> max{f[n+1][所有状态][1]} -> 第n行随便放,第n+1行没有。
*/
#include<bits/stdc++.h>
using namespace std;
int n,m,f[110][70][70];//i -> state I && state I-1
bool mmap[110][20];//1 -> able to put
int state[110];//state -> ont line's all the H's State
int sum[70],st[70],tot,ans; number
int quary(int s){int cnt=0;while(s) cnt+=(s&1),s>>=1;return cnt;}
int main()
{
//freopen("1.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
string s;cin>>s;
for(int j=0;j<m;j++){
mmap[i][j]=(s[j]=='P');
if(!mmap[i][j]) state[i]|=(1<<j);
}
char ch=getchar();
}
for(int j=0;j<(1<<m);j++)
if(!(j&(j<<1))&&!(j&(j<<2))) st[++tot]=j,sum[tot]=quary(j);
for(int i=1;i<=tot;i++)//not H
if(!(st[i]&state[1])) f[1][1][i]=sum[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=tot;j++)//line i-1;
for(int k=1;k<=tot;k++)
if(f[i][j][k]&&!(st[j]&st[k]))//do not attact one another
for(int l=1;l<=tot;l++){//think of (line i+1)'s state
if(!(st[l]&st[j])&&!(st[l]&st[k])&&!(st[l]&state[i+1]))//can
f[i+1][k][l]=max(f[i+1][k][l],f[i][j][k]+sum[l]);
}
for(int i=1;i<=tot;i++)
ans=max(ans,f[n+1][i][1]);
cout<<ans;
return 0;
}
前推后
一维点,一维状态
然后枚举所有状态
树形dp
一般递归来写
//战略游戏
void dfs(int cur,int fa)
{
f[cur]=1,g[cur]=0;
for(int i=first[cur];i;i=next[i]){
int t=to[i];
if(t==fa) continue;
dfs(t,cur);
f[cur]+=min(f[t],g[t]);
g[cur]+=f[t];
}
}
或者
//选课
int dp(int i,int j)
{
if(f[i][j]) return f[i][j];
if((i==0&&bj)||j==0) return 0;
if(!child[i][0]&&!child[i][1]) return fs[i];
bj=true;
if(child[i][1]) f[i][j]=dp(child[i][1],j);
for(int k=0;k<=j-1;k++)
f[i][j]=max(f[i][j],dp(child[i][0],k)+dp(child[i][1],j-k-1)+fs[i]);
return f[i][j];
}
一般转成二叉树,这样就只有两种情况
(child[i][0]&&child[i][1])
如何转呢
左儿子,右兄弟
for(int i=1;i<=n;i++){
int fa=father[i];
if(!son[fa]) child[fa][0]=i;//如果父亲还没有儿子,他的左儿子是i
else child[son[fa]][1]=i;//否则他的其他儿子接在他儿子的右儿子上
son[fa]=i;//更新他的最后一个儿子
}
这样保证一个节点的右儿子一定是兄弟
6.分治
简单的分治
https://blog.csdn.net/sslz_fsy/article/details/81273296
CDQ
https://blog.csdn.net/sslz_fsy/article/details/81292590