刷版子·总结(待补全......)

强势安利black学长板子
http://blog.csdn.net/loi_black/article/details/53161216#t18

最短路:

spfa

//期望的时间复杂度为o(ke),k为所有顶点的进队次数,k一般<=2 
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define maxn 1000001
#define inf 0x7ffffff
using namespace std;
//尽量用nxt别用next,有歧义 
int n,m,tot;
int head[maxn],next[maxn],dist[maxn];
bool vis[maxn];

struct node
{
    int from,to,w;
}e[maxn];

void add(int from,int to,int w)
{
    e[++tot] = (node){from,to,w};
    next[tot] = head[from];
    head[from] = tot;
/*    e[t].from=i;
    e[t].to=j;
    e[t].w=w;
    e[t].next=head[i];
    head[i]=++t;
*/
}
//先将源点入队,再依次取与当前点(已经处理完最短路的点)相邻的点 
void spfa(int s)
{
    queue <int> q;
    for(int i=1;i<=n;i++) dist[i]=inf;   
    q.push(s);
    vis[s]=true;
    dist[s]=0;
    while(!q.empty())
    {//通过for循环解决的是与u相邻的所有点与边,即:处理(取出)一个点,就用for循环更新完与他相邻的点的zdl 
        int u=q.front();
        q.pop();
        vis[u]=false;
        for(int i=head[u];i!=-1;i=next[i])
        {//从以源点为起点的边开始循环,e[i].next:当前边的下一条边(同一个起点)
            int v=e[i].to;
            if(dist[v]>dist[u]+e[i].w)
            {
                dist[v]=dist[u]+e[i].w;
                if(!vis[v])//没有在队列中(是否之前入队过,又出队,vis才==0??????) 
                {
                    q.push(v);
                    vis[v]=true;                             
                }
            }
        }
    }
}

int main()
{
    freopen("spfa.in","r",stdin); 
    freopen("spfa.out","w",stdout); 

    int a,b,c,s,e; 
    //t=0; 
    memset(head,-1,sizeof(head)); //head初始化一定要在 add(a,b,c)之前 
    scanf("%d%d",&n,&m); 
    for(int i=1;i<=m;i++) 
    {
        scanf("%d%d%d",&a,&b,&c); 
        add(a,b,c); 
        //add(y,x,z); 无向图的话建双向边 跑两边,也没必要,倒回来走会慢,如果双向边边权不一样就得建了吧 
        //!!!不影响,因为是一直朝着终点走,一条边不会走两遍  
    } 
    //求s->e的最短路(zdl)
    scanf("%d%d",&s,&e); 
    spfa(s); 
    if(dist[e]==inf) printf("-1\n"); 
    else printf("%d",dist[e]); 

    return 0;
}


/* 带负权的有向图,不能用Dijkstra算法,因为贪心算法(Dijkstra )
只看到了当前的最小值,有可能在另一条未选择的路的下一步是一个很大的负值,而此时我们已经把他加到树里边,
此时就会找到错误答案。!!! SPFA算法适用于不带负权环的,带负权的有向图和不带负权的无向图,
因为无向图中若有一条边为负,即相当于这两点形成负权环。!!!   算法的核心是广搜,
同时用一个数组保存从第一个节点到其他节点的最短路,首先把第一个节点入队列,第一个节点出队列,
更新以第一个节点为始点的所有点在最短路数组里的值,若更新成功且该点没在队列中,该点入队。
*/ 

+SLF优化

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define maxn 20000+10
#define inf 10001
using namespace std;

int n,m,tot=0;
int dis[maxn],head[maxn],next[maxn],vis[maxn];

struct node{
    int f,t,w;
}e[maxn];

void add(int f,int t,int w)
{
    e[++tot]=(node){f,t,w};
    next[tot]=head[f];
    head[f]=tot;
    return;
}

deque <int> q;
void spfa(int x)
{   
    while(!q.empty()) q.pop_front();
    memset(vis,0,sizeof(vis));  
    memset(dis,inf,sizeof(dis)); 

    q.push_back(x);
    vis[x]=1; dis[x]=0;
    //q.push_back(0);//如果没有后面的特判的话,push 0进去可防止v与队首元素比较是找不到队首元素,访问队头元素却找不到队头,会出现exe停止运行等程序错误

    while(!q.empty())
    {
        int u=q.front();
        q.pop_front();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=next[i])
        {
            int v=e[i].t; 
            if(dis[v]>dis[u]+e[i].w)
            {
                dis[v]=dis[u]+e[i].w;
                if(vis[v]==0)
                {
                    if(q.empty()) q.push_front(v);
                    else
                    {
                        if(dis[v]>dis[q.front()]) q.push_back(v);
                        else q.push_front(v);
                    }
                    vis[v]=1;
                    //dis[v] < dis[q.front()] ? q.push_front(v) : q.push_back(v);
                }       
            }
        }
    }
    return;
}

int main()
{
    int a,b,c,s,e;
    memset(head,-1,sizeof(head));
    scanf("%d%d%d%d",&n,&m,&s,&e);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    spfa(s);
    printf("%d\n",dis[e]);

    return 0;
}

spfa判负环
bfs

//maxn<=200001时(具体多少没有测试过)就TLE --->洛谷P3385 判负环
#include<iostream> 
#include<cstdio> 
#include<cstring> 
#include<deque> 
#include<queue> 
#define maxn 200001 
using namespace std; 

int n,m,t=0,tot=0,num[maxn]; 
int dis[maxn],first[maxn],nxt[maxn]; 
bool vis[maxn]; 

struct node{ 
    int f,t,d; 
}e[maxn*2]; 

deque <int> q;  
void add(int f,int t,int d) 
{ 
    e[++tot]=(node){f,t,d}; 
    nxt[tot]=first[f]; 
    first[f]=tot; 
} 

bool spfa(int x)
{ 
    for(int i=1;i<=n;i++) dis[i]=0x3f3f3f; 
    memset(vis,false,sizeof(vis)); 
    while(!q.empty()) q.pop_front(); 

    q.push_front(x); 
    vis[x]=true; 
    dis[x]=0; 
    while(!q.empty()) 
    { 
        int u=q.front(); 
        q.pop_front(); 
        vis[u]=false; 
        for(int i=first[u];i!=-1;i=nxt[i]) 
        { 
            int v=e[i].t; 
            if(dis[v]>dis[u]+e[i].d) 
            { 
                dis[v]=dis[u]+e[i].d; 
                if(!vis[v]) 
                { 
                    if(++num[v]>n) return false; 
                    if(q.empty()) q.push_front(v); 
                    else 
                    { 
                        if(dis[v]>dis[q.front()]) q.push_back(v);  
                        else q.push_front(v); 
                    } 
                }  
            } 
        } 
    } 
    return true; 
} 

int main() 
{ 
    freopen("fuhuan.in","r",stdin); 
    freopen("fuhuan.out","w",stdout);  

    int a,b,w; 
    scanf("%d",&t); 
    while(t--) 
    { 
        memset(first,-1,sizeof(first));
        memset(num,0,sizeof(num));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        { 
            scanf("%d%d%d",&a,&b,&w); 
            if(w < 0) add(a,b,w); 
            else { add(a,b,w); add(b,a,w); } 
        } 
        if(!spfa(1)) printf("YE5\n"); 
        else printf("N0\n"); 
    } 
    return 0; 
}  

dfs

//比bfs快
//负圈又称负环,就是说一个全部由负权的边组成的环,这样的话不存在最短路,因为每在环中转一圈路径总长就会变小 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<iomanip>
#define maxn 200001
using namespace std;

int n,m,t,tot,a,b,w;   
int dis[maxn],first[maxn],nxt[maxn*2];//nxt[]开两倍!!!  
bool vis[maxn],flag;

struct node{
    int f,t,d;
}e[maxn*2];

inline int read()
{ 
    char ch=getchar(); 
    int x=0,f=1; 
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } 
    while(ch>='0'&&ch<='9') { x=x*10+ch-48; ch=getchar(); } 
    return x*f;
}

void add(int f,int t,int d)
{
    e[++tot]=(node){ f,t,d };
    nxt[tot]=first[f];
    first[f]=tot;
}

void spfa(int u)
{
    int v;
    vis[u]=true;
    for(int i=first[u];i;i=nxt[i])
    {
        v=e[i].t;
        if(dis[v]>dis[u]+e[i].d)
        {
            dis[v]=dis[u]+e[i].d;
            if(vis[v]||flag)
            {
                flag=true;
                break;
            }
            spfa(v);
        }
    }
    vis[u]=false; 
}

int main()
{
    freopen("fuhuan.in","r",stdin);
    freopen("fuhuan.out","w",stdout);

    t=read();
    while(t--)
    {
        flag=false; tot=0;
        n=read();m=read();
        for(int i=1;i<=n;i++) { dis[i]=0;vis[i]=0;first[i]=0; }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&w);
            add(a,b,w);
            if(w>=0) add(b,a,w);
        }
        for(int i=1;i<=n;i++)
        {
            spfa(i);
            if(flag) break;
        }
        if(flag)
            printf("YE5\n");
        else
            printf("N0\n");
    }
    return 0; 
}
//初始化为0,能往下拓展的只能是负边,那就记录一下 
//从一开始从起点开始更新的时候就是只有遇到负边的时候才会更新  
//又遇到了这个点,说明转了一圈回来,而且只有这一圈上的所有边都为负边才能更新过来,所以说明存在负环  

dijkstra

#include<iostream>
#include<cstdio>
#define Max 0x3fffffff
int map[1005][1005];
int dis[1005];

void dijkstra(int n)
{
    int visit[1001]={0};
    int min,i,j,k;
    visit[1]=1;
    for(i=1;i<n;++i)
    {
        min=Max;
        k=1;
        //找与当前点相连的,并且离起点最近的点  
        //--->不一定与当前点相连,因为只要是离起点最近,除非都是maxn,不然一定是<maxn的,也就是更新过了的
        //一定能保证从它到起点这段路上所有距离都是更新为最小值的,而且我们从起点开始更新,
        //一层一层拓展出去,一定是先更新离起点近的某一个点或是几个点,再更新后面的
        // “一个点或是几个点”:因为每次找离起点最近的,有可能间接到起点比直接到近,间接与间接之间,
        //直接与直接之间也有远近之分,但是我们每次找的只是那个最短的  
        for(j=1;j<=n;++j)
        {
            if(!visit[j]&&min>dis[j])
            {
                min=dis[j];
                k=j;
            }
        }
        visit[k]=1;
        //用新找到的这个最近的点来更新与它相邻的点:最近的点有更大的可能以最小的距离更新完所有的点 
        //或者是一部分点  
        //只有相邻 map[k][j] 才不可能为初始值Max,dis[j]才能>dis[k]+map[k][j],才可以被更新  
        for(j=1;j<=n;++j)
        {
            if(!visit[j]&&dis[j]>dis[k]+map[k][j])
                dis[j]=dis[k]+map[k][j];
        }
    }
    printf("%d\n",dis[n]);
}

int main()
{
    int t,n,i,j,from,to,cost;
    while(scanf("%d%d",&t,&n)!=EOF)
    {
        for(i=1;i<=n;++i)
        {
            map[i][i]=0;
            for(j=1;j<i;++j)
                map[i][j]=map[j][i]=Max;
        }
        for(i=1;i<=t;++i)
        {
            scanf("%d%d%d",&from,&to,&cost);
            if(cost<map[from][to])    //--->不写也行,直接赋值,覆盖掉原来的初始值Max            
                map[from][to]=map[to][from]=cost;
        }
        for(i=1;i<=n;++i)
            dis[i]=map[1][i];//dis初始化为起点到i点的距离,与起点不直接相邻(相连),保持初始化map时的极大值Max 
        dijkstra(n);
    }
    return 0;
}

归并排序求逆序对

有注释版:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;

const ll maxn=100000+10;
ll a[maxn],b[maxn];
ll n,total;

void msort(ll l,ll r)
{ 
    ll mid=l + r >> 1;
    if(l<r) //还没有分到最简单的状态(l=r),没有分到底,也就是剩一个元素的时候 
    {//如果包含l=r的话,会无限循环,且l=r是最简单的状态,是停止递归的条件 ,此时开始从最后一个递归调用返回 --->from baike 
        msort(l,mid); 
        msort(mid+1,r); //这两个递归可以当做是处理 寻找范围 的一种方法 ,按顺序计算出下一步要求哪一个区间的逆序对 
    }  

    //每一部分都会被分成两部分考虑到,不用担心逆序对个数的遗漏 如:
    //3 2 1 4 6 5被分为 3 2 1 和 4 6 5 两部分,同样的,3 2 1 和 4 6 5 也会各自被分为3 、2 1 和 4 、6 5两部分,
    //数组以1开头的话,则是被分为 3 2 、1 和 4 6 、5两部分 
    //以此类推 ,3 2 1 和 4 6 5这两部分内部的逆序对也会被找出,不会产生遗漏  

    ll i=l,k=l,j=mid+1;//分成两部分 
    while(i<=mid&&j<=r)
    { 
        if(a[i]<=a[j]) b[k++]=a[i++];//a中的当前区间处理出的顺序(每次存小的那个+下面的while循环中处理的)依然存在b的相应区间  
                                     //(保证未被操作(未被更新)的数不受影响)  
        //b中存的是两部分中较小的那个,a[i]小,存入b,i++,a[j]与上一部分的下一个(i+1)比较 
        else 
        { 
            total+=mid-i+1;//i后面的(i~mid)+i自己(i本身) //后面一部分的元素小,找到一个“前面大于后面的”,逆序对个数增加  
            b[k++]=a[j++];//记录较小的 
        } 
    } 
    while(i<=mid) b[k++]=a[i++]; //j这边的已经放完了,i那边的还有,说明i这边的比较大,把剩下的放进去就好  ① 
    while(j<=r) b[k++]=a[j++];  //i这边的已经放完了,j那边的还有,说明j这边的比较大,把剩下的放进去就好  ②  
    for(ll i=l;i<=r;i++) a[i]=b[i]; //把排好序(可能是部分排好序)的b中的值再赋回a中  (//只更新改的这一部分 ) 
    //把排好序(可能是部分排好序--->通过while把逆序对中小的存到大的之前,
    //以前找到的逆序对消失,不会产生重复查找的事情(一个逆序对被多次找出并记录的情况不会发生。))的b中的值再赋回a中,再对a 
    //进行查找,重复上述操作   
    //另外,①与②只会出现一个(只会有一个满足/成立) 
}  

int main() 
{ 
    scanf("%d",&n); 
    for(ll i=1;i<=n;i++) 
    { 
        scanf("%d",&a[i]); 
    } 
    msort(1,n); 
    printf("%lld\n",total); 

    return 0; 
} 

/*
重点注意:why total+=mid-i+1
以4 5 1 6 2 3为例 
我们先分析递归调用的过程,每次一分为二:
4 5 1①--->4 5②,1--->到了4,5,1递归就停止了... 
6 2 3③--->6 2,④1--->到了6,2,1递归就停止了...

递归停止后, 开始从最后一个递归调用返回④--->③--->②--->① 所以是先处理有半部分区间,再处理左半部分区间,也就是说,在处理 4 5 1 6 2 3
这个大区间时,左右两个子区间都是有序的,而且是从小到大排序的,所以如果找到一个逆序对(一定是整个区间的逆序对,所有子区间的逆序对 
已经处理完了,并且小的在前,大的在后,也就是上面提到的“消失了”,最后才处理最大的原来的那个区间)因为大区间是最后处理的,
而且处理前小区间的逆序对已找出,不会有遗漏,而且两个小区间还有序,所以上面提到的 “一定是整个区间的逆序对”的意思就是说,如果有逆序对,
也就是前一个>后一个,那么这个“前一个”一定在左区间,也就是前面的那个区间,这个“后一个”一定在右区间,也就是后面那个区间
那便可以解释 total+=mid-i+1的含义:
在大区间未处理之前,我们的原序列现在为:1 4 5 | 2 3 6,我们可以搜到4和2构成了一对逆序对,又因为每个区间都是递增的,那么,4所在的
那个区间中,在4之后的数(这里只有5),与2肯定也能形成一堆逆序对,毕竟都比4大了,肯定比2大,所以total所加的值,也就是新找到的
逆序对的个数是包括4在内(+1)的4后面所有数的个数的总和(mid-i(i为4的坐标,mid是左区间的右边界)),所以就能得出mid-i+1来了,所以:
total+=mid-i+1 

另外,各个小区间的处理方法与大区间相同,只不过是早处理一点,所以说,total+=mid-i+1也同样适用于各个小区间 

*/

无注释版:
(好吧,有一丢丢注释♪(^∇^*))

#include<iostream>
#include<cstdio>
#define maxn 100010
#define ll long long
using namespace std;

ll n,a[maxn],b[maxn],ans=0;

void gbpx(ll l,ll r)
{
    ll mid=(l+r)/2;
    if(l<r)
    { 
        gbpx(l,mid);
        gbpx(mid+1,r);
    } 
    ll i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r)//"<="是因为有可能i/j的最后一个也<对方的最后一个或者是某个数,但是最后一定有一方至少剩下一个,最大的那些个没有一个更大的使其被存进b数组中,那肯定就留下了呗,然后再用下面的while循环来存储他们 
    { 
        if(a[i]<=a[j]) 
        {
            b[k++]=a[i++];
        }
        else
        {
            b[k++]=a[j++]; ans+=mid-i+1;
        }
    } 

    while(i<=mid) b[k++]=a[i++];//"<="是因为有可能最后一个剩着(当前的区间中相比之下最大的那个数)     
    while(j<=r) b[k++]=a[j++];    
    for(int i=l;i<=r;i++) a[i]=b[i];      
} 

int main() 
{ 
    scanf("%lld",&n); 
    for(ll i=1;i<=n;i++) scanf("%d",&a[i]); 
    gbpx(1,n); 
    printf("%lld\n",ans);     
    return 0; 
} 
//归并排序的话就把输出tot改为输出a数组即可,而且tot也不用记了 ^3^ 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值