NOIP 2016[临时抱佛脚系列]各类基本算法拓展

------------by NK SuperGate

许多基本的算法我们都已经烂熟于心(有向边的tajan,dp基本模型等等),但是我们如何利用这些算法去搞一些更高端的事情出来呢?

这个时候我们就需要将这些算法进行改进或者优化,从而得到一种适合题目的算法,今天要复习的就是对几类算法的拓展以及优化,从而使得这些算法能够适应各种相似题型

Tajan算法

Tajan算法求有向图的强连通分量是非常经典的一个模型。但是Tajan是否能够应用于无向图呢?

事实上是可以的,Tajan在无向图中可以很快的求出无向图的割点和割边,原理和求有向图的强连通分量相似

这里直接放出这两种的代码(我也不会推理和证明),代码和原本的Tajan只有几句差异

void tajan(int x,int fa){//tajan求割点 
	dfn[x]=low[x]=++vistime;
	int i,v;
	for(i=last[x];i>=0;i=s[i].Next){
		v=s[i].b;
		if(dfn[v]==0){
			tajan(v,x);
			low[x]=min(low[x],low[v]);
			if(low[v]>=dfn[x])
				if(fa!=0)cutp[x]=1;
		}
		else if(v!=fa)low[x]=min(low[x],dfn[v]);
	}
	if(fa==0&&cnts[x]>=2)cutp[x]=1;
}
void tajan(int x,int fa){//tajan求割边 
	dfn[x]=low[x]=++vistime;
	int i,v;
	for(i=last[x];i>=0;i=s[i].Next){
		v=s[i].b;
		if(dfn[v]==0){
			tajan(v,x);
			low[x]=min(low[x],low[v]);
			if(low[v]>dfn[x])iscutl[i]=1;
		}
		else if(v!=fa)low[x]=min(low[x],dfn[v]);
	}
}

最长上升子序列问题

我们设f[i]表示以i结尾的最长上升子序列长度,容易得到f[i]=max{f[j]}+1 a[j]<a[i]&&j<i,显然复杂度为O(n^2)

我们接着分析:对于两个状态f[a]==f[b]并且a[a]<a[b],那么对于后面的所有状态来说,a一定不会比b更差,因为如果a[b]<a[i]那么a也同样满足,但是反过来却不一定,因为a在b的前面,a有更多的选择去得到最优解

因此我们对于两个最优值相同的状态只需保存结尾的数更小的即可

用g[i]表示f为i的最小状态编号可以发现g数组一定是不递减的

g是动态的,对于一个给定的状态i,我们只需考虑之前已经计算过的状态j(j<i),给定一个状态时我们可以用二分查找找到第一个满足g[k]>=a[i]的k,则f[i]=k此时a[i]<g[k]且d[i]=k,所以更新g[k]=a[i],时间复杂度为O(nlogn)

memset(g,inf,sizeof(g));
	for(int i=1;i<=n;i++){
		int k=lower_bound(g+1,g+1+n,a[i])-g;
		f[i]=k;
		g[k]=a[i];
	}


分层图与最短路

每天,农夫John需要经过一些道路去检查N号牛棚里面的牛.
农场上有M条双向泥土道路,编号为1..M ,经过一条道路需要花费一定的时间。John想更新一些路经来减少每天花在路上的时间.具体地说,他想更新K 条道路,将它们所须时间减为0.帮助John选择哪些路经需要更新使得从1到N的时间尽量少。
1<=N<=10000
1 <= K <= 20   1<=M<=50,000


dp[i][t]表示从起点到i号点,所需最短时间。其中修改t了条道路

dp[i][t]=min{ //j是与i直接有边相连的点,下面讨论从j到i的情况
                             dp[j][t]+Len[j][i] //决策1:直接从j走到i耗时Len[j][i]
                             dp[j][t-1] //决策2:将j到i的边长度修改为0,j走到i耗时0

}0<=t<=k

#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=10005,inf=0x3f;
int last[maxn],n,m,k,f[maxn][25];
bool vis[maxn][25];
inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
struct wr{
    int a,b,c,NEXT;
    wr(int a,int b,int c,int NEXT):a(a),b(b),c(c),NEXT(NEXT){}  
};
struct wk{
    int x,t,v;
    wk(int x,int t,int v):x(x),t(t),v(v){}  
};
bool operator <(wk a,wk b){return a.v>b.v;}
priority_queue<wk>q;
vector<wr>s;
void insert(int a,int b,int c){
    s.push_back(wr(a,b,c,last[a]));
    last[a]=s.size()-1;
    s.push_back(wr(b,a,c,last[b]));
    last[b]=s.size()-1;
}
void dijkstra(){
    memset(f,inf,sizeof(f));
    int v,len,i;
    q.push(wk(1,0,0));
    f[1][0]=0;
    while(q.size()){
        int x=q.top().x,t=q.top().t;
		q.pop();
		if(vis[x][t])continue;
		vis[x][t]=1;
		for(i=last[x];i>=0;i=s[i].NEXT){
			v=s[i].b,len=s[i].c;
			if(f[v][t]>f[x][t]+len){
				f[v][t]=f[x][t]+len;
				q.push(wk(v,t,f[v][t]));
			}
			if(f[v][t+1]>f[x][t]&&t+1<=k){
				f[v][t+1]=f[x][t];
				q.push(wk(v,t+1,f[v][t+1]));
			}
		} 
    }
}
int main(){
	memset(last,-1,sizeof(last));
    _read(n);_read(m);_read(k);
    int i,x,y,z;
    for(i=1;i<=m;i++){
        _read(x);_read(y);_read(z);
        insert(x,y,z);
    }
    dijkstra();
    cout<<f[n][k];
} 

最小环问题

一般来说在范围允许的情况下,最小环问题我们用floyd解决

memcpy(dis,map,sizeof(map));
   answer=inf;
   for(k=1;k<=n;k++){
      for(i=1;i<=k-1;i++)
        for(j=i+1;j<=k-1;j++)
            answer=min(answer,dis[i][j]+map[i][k]+map[k][j]);
      for(i=1;i<=n;i++)
         for(j=1;j<=n;j++)
             dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
   }

这里map[i][j]为i,j边的长度

一个环中的最大结点为k(编号最大),与它直接相连的两个点为i,j,这个环的最短长度为map[i][k]+map[k][j]+i到j的路径中,所有结点编号都小于k的最短路径长度
根据floyd的原理,在最外层循环做了k-1次之后,dis[i][j]则代表了i到j的路径中,所有结点编号都小于k的最短路径
综上所述,该算法一定能找到图中最小环


总之就写到这里了,明天NOIP还得早起……NOIP前最后一篇Blog

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值