暑假总结

上了半个暑假的课,马上结束了

老师叫我们写个总结

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

7.堆

https://blog.csdn.net/sslz_fsy/article/details/81303003

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/81293673
今日推荐