模板整理(一)

来源:互联网 发布:影后春夏 知乎 编辑:程序博客网 时间:2024/06/05 02:15

1并查集(8-2)

int find(int x)                                                                  //查找我(x)的掌门

{

    int r=x;                                                                       //委托 r 去找掌门

    while (pre[r ]!=r)                                                     //如果r的上级不是r自己(也就是说找到的大侠他不是掌门 = =)

    r=pre[r ] ;                                                                   // r 就接着找他的上级,直到找到掌门为止。

    return  r ;        //掌门驾到~~~


    int i=x, j; (合并)

    while(i!=r) 

    {

j=pre[i];

pre[i]=r;//  r是不是最后的父亲节点?

r=j;

    }                                                        

}


void join(int x,int y)                                                                   //我想让虚竹和周芷若做朋友

{

    int fx=find(x),fy=find(y);                                                       //虚竹的老大是玄慈,芷若MM的老大是灭绝

    if(fx!=fy)                                                                               //玄慈和灭绝显然不是同一个人

    pre[fx ]=fy;                                                                           //方丈只好委委屈屈地当了师太的手下啦

}


2深度优先搜索(dfs)和 广度优先搜索 (bfs)

dfs/

void dfs(int x,int y)

{if(x<1||x>m||y<1||y>n)return ;// 函数返回值为空if(s[x][y]!='@')//这个if语句的作用很大return ;s[x][y]='*';//标记 for(int i=-1 ; i<=1 ; i++)for(int j=-1 ; j<=1 ; j++) //八个方向{//也有另外一种写法  即用数组表示dfs(x+i,y+j);}//这一步递归真的好厉害}



3.快速幂

 //快速幂
  1. int pow3(int a,int b)  
  2. {  
  3.     int ans = 1,base = a;  
  4.     while(b!=0)  
  5.     {  
  6.         if(b&1)  //?b==1
  7.             ans *= base;  
  8.         base *= base;  
  9.         b>>=1;  //b/2
  10.     }  
  11.     return ans;  
  12. }  

4.素数打表

筛选法求素数也重要的求素数方法之一。这种方法主要用于打素数表,如求出n之内的所有素数,其思路是从1开始遇到一个素数就标记一下,并去掉n之内的大于它的所有倍数,直循环到n:#include<stdio.h>int n,i,j,a[1000001],p[100000],t=0;void main(){    scanf("%d",&n);    a[1]=0;    for(i=2;i<=n;i++)a[i]=1;    for(i=2;i<=n;i++)        if(a[i]){            p[t++]=i;//i即为被记录的素数           for(j=i+i;j<=n;j+=i)a[j]=0;//素数的倍数都标记为零        }    for(i=0;i<t;i++)        printf("%d%c",p[i],i<t-1?' ':'\n');}此方法也有局限性,数据量中等时才不会超时,数据量过大时也会超时,而且只能用素数打表,不能对单个数进行判定!3)这是我根据《离散数学》上的一个定理想到的,定理为:若正整数a>1,a不能被不超过a的平方根的任一素数整除,则a是素数,实现过程如下:#include<stdio.h>#include<math.h>int p[1000000],a[10000001],t=0;int prime(int n){    int i,q;    q=(int)sqrt(n);    for(i=0;p[i]<=q&&t;i++)        if(n%p[i]==0)return 0;//要不要初始化p【0】?    return 1;}int main(){    int n,i;    scanf("%d",&n);    for(i=2;i<=n;i++)        if(prime(i))p[t++]=i;//这个地方的i,即为上面函数的实参    for(i=0;i<t;i++)//这个if语句能不能实现->运行了一下可以实现惊恐        printf("%d%c",p[i],i<t-1?' ':'\n');}此方法可以对超大量数据的进行打表!此方法也同样合适于素数打表,判定单个时这个方法不可取!


5. gcd   lcm


int gcd(int a,int b)
{
   return b ? gcd(b,a%b) : a;//递归
 }


6. 二分



7   栈和队列



8   贪心



9 最小生成树再见

克鲁斯卡尔算法 (找权重最小的边)

struct node //结构体 
{
int from,to,len;
}edge[maxn];
int n,fa[maxn],m,ans,q;
bool cmp(node a,node b)
{
return a.len<b.len;
}
int Find(int x)
{
if(fa[x]==x)
return x;
return fa[x]=Find(fa[x]);//并查集
}
void Merge(int x,int y)
{
x=Find(x),y=Find(y);
if(x!=y)
fa[y]=x;
}

(此处为模板,上面是可能会用到的一些函数)

int Kruskal()//最小生成树 
{

sort(edge,edge+m,cmp);

//对边进行排序,同时整个结构体的元素随着边的排序而排序 

for(int i=0;i<n;i++)//初始化 
fa[i]=i;
ans=0;
for(int i=0; i<m ;i++)
if(Find(edge[i].from)!=Find(edge[i].to))//连接   避免成环 这步很重要
{
Merge(edge[i].from,edge[i].to);
ans+=edge[i].len;//计数 

}
return ans;
}



普利姆算法

  1. void prim(){  
  2.     for(i=1;i<=n;i++){  
  3.         dis[i]=map[1][i];  
  4.         pre[i]=-1;  //map存两点距离,dis存未收入点到收入点距离,pre存该点的父结点 
  5.     }  
  6.     dis[1]=0;  
  7.     pre[1]=0;  
  8.     int sum=0,flag,minn;  
  9.     for(i=2;i<=n;i++){  
  10.         minn=inf;  
  11.         for(j=1;j<=n;j++){  
  12.             if(pre[j]==-1&&dis[j]<minn){  
  13.                 flag=j;  
  14.                 minn=dis[j];  
  15.             }  
  16.         }  
  17.             pre[flag] = i;  
  18.             sum+=minn;  
  19.             for(j=1;j<=n;j++){  
  20.                 if(pre[j]==-1&&dis[j]>map[flag][j])  
  21.                 //更新未收入点到已收入点的最短距离   
  22.                 dis[j]=map[flag][j];  
  23.             }  
  24.     }  
  25.     printf("%d\n",sum);  
  26. }   



10·博弈


11·LIS(最长上升子序列)

#include <iostream> 
#include <cstring> 
#include <algorithm> 
using namespace std; 
const int  MAXN =1010; 
int a[MAXN];   int maxLen[MAXN]; 
int main() 
{   
  int N;    cin >> N; 
  for( int i = 1;i <= N;++i) 
   {  
    cin >> a[i];    maxLen[i] = 1;  //初始化
   } 
  for( int i = 2; i <= N; ++i) 
 {  
  //每次求以第i个数为终点的最长上升子序列的长度 
    for( int j = 1; j < i; ++j)   
    //察看以第j个数为终点的最长上升子序列 
      if( a[i] > a[j] ) 
        maxLen[i] = max(maxLen[i],maxLen[j]+1);  //状态转移方程
  } //内层循环确保j是动态变化的
  cout << * max_element(maxLen+1,maxLen + N + 1 ); //且记录最长的那个上升序列
  return 0;  //有趣的输出方法
} //时间复杂度O(N2) 
“人人为我”递推型动归程序 


//O(n^2)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 30010;
int dp[maxn], a[maxn];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<n;++i)  
            scanf("%d",&a[i]);  
        int ans=0;  
        for(int i=0;i<n;++i)  
        {  
            dp[i]=1;  
            for(int j=0;j<i;++j)  
            {  
                if(a[j]<a[i])  
                    dp[i] = max(dp[i], dp[j]+1);  
            }   
            ans=max(dp[i],ans);  
        }  
        printf("%d\n",ans);  
}
return 0;



//O(nlogn)
#include <cstdio>  
#include <algorithm>  
#define INF 0x3f3f3f  
using namespace std;  
int dp[30010],a[30010];  
int main()  
{  
    int n,i,j;  
    while(scanf("%d",&n)!=EOF)  
    {  
        for(i=0;i<n;++i)  
        {  
            scanf("%d",&a[i]);  
            dp[i]=INF;  
        }  
        for(i=0;i<n;++i)  
            *lower_bound(dp,dp+n,a[i])=a[i];  
        printf("%d\n",lower_bound(dp,dp+n,INF)-dp);   
    }   //lower_bound()在last和first之间进行二分查找
前闭后开 
    return 0;   //返回第一个大于或等于val的位置
}   
 


12.LDS(最长公共子序列)

#include <iostream> 
#include <cstring> 
using namespace std; 
char sz1[1000]; 
char sz2[1000]; 
int maxLen[1000][1000];  //maxLen[i][j]表示第一个串的前i个数字
int main() { //与第二个串的前j个数字公共序列的数目
  while( cin >> sz1 >> sz2 ) { 
    int length1 = strlen( sz1); 
    int length2 = strlen( sz2); 
    int i,j; 
    for( i = 0;i <= length1; i ++ )  //初始化
      maxLen[i][0] = 0; 
    for( j = 0;j <= length2; j ++ ) 
      maxLen[0][j] = 0;
 
    for( i = 1;i <= length1;i ++ )  { 
        for( j = 1; j <= length2; j ++ ) { 
      if( sz1[i-1] == sz2[j-1] ) 
           maxLen[i][j] =   maxLen[i-1][j-1] + 1; 
      else  
           maxLen[i][j] = max(maxLen[i][j-1],  //取最大的一个
        maxLen[i-1][j]); 
      } 
    } 
    cout <<  maxLen[length1][length2] << endl; 
  } 
  return 0;  



13.树状数组

#include<cstdio>
#include<cstring>
#define lowerBit(x) (x&(-x))
int num[10000];
int n;
void UpDate(int x,int y) //把第x个数加y
{
while (x <= n)
{
num[x] += y;
x = x+lowerBit(x);
}
}
int Query(int x) //求从1到x的和 
{
int sum = 0;
while (x > 0)
{
sum += num[x];
x = x-lowerBit(x);//x&(x-1)
}
   return sum;
}
int main()
{
scanf ("%d",&n);
memset(num,0,sizeof(num));
for (int i = 1 ; i <= n ; i++)
{
int t;
scanf ("%d",&t);
UpDate(i,t);
}

int m;
scanf ("%d",&m);
while (m--)
{
int x,y;
scanf ("%d %d",&x,&y);//看不懂疑问
printf ("%d\n",Query(y)-Query(x-1));
}
   return 0;
}




14.线段树 //看不懂

struct Node//不要左右子节点指针的做法 
{
int L,R;
int minV,maxV;
int Mid()
{
return (L+R)/2;//还有这操作惊恐
}
};
Node tree[800010]; //4倍叶子节点数量就够 


void BuildTree(int root, int L, int R)
{
tree[root].L = L;
tree[root].R = R;
tree[root].minV = INF;//这是为什么?到底哪个是大的

tree[root].maxV = -INF;//那个是小的?
if(L!=R)
{
BuildTree(2*root+1, L, (L+R)/2);
BuildTree(2*root+2, (L+R)/2+1, R);
}
}


void Insert(int root, int i, int v)
//将第i个数,其值为v,插入线段树 
{
if(tree[root].L == tree[root].R)//成立则亦有tree[root].R==i 
{
tree[root].minV = tree[root].maxV = v;
return ;
}
tree[root].minV = min(tree[root].minV , v);
tree[root].maxV = max(tree[root].maxV , v);
if(i <= tree[root].Mid())
Insert(2*root + 1, i, v);
else
Insert(2*root + 2, i, v);
}


void Query(int root, int s, int e)
//查询区间[s,e]中的最大值和最小值,如果更优就记录在全局变量里 
{
if( tree[root].minV >= minV && tree[root].maxV <= maxV )
return;
if( tree[root].L == s && tree[root].R == e )
{
minV = min( minV, tree[root].minV );
// minV = min( minV, tree[root].minV );
maxV = max( maxV, tree[root].maxV );
return ;
}
if( e <= tree[root].Mid() )
Query( 2*root + 1, s, e );
else if( s > tree[root].Mid() )
Query( 2*root + 2, s, e );
else
{
Query( 2*root + 1, s, tree[root].Mid() );
Query( 2*root + 2, tree[root].Mid() + 1, e );

}

}



15.KMP

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//S: a b a b b a b a b a d a b a b a a a b a b a a
//                                               j
//                      T: a b a b a a a b a b a a
//                  next :-1 0 0 1 2 3 1 1 2 3 4 5
char s[1000], t[1000];
int next[1000] = {-1, 0, 0, 1, 2, 3, 1, 1, 2, 3, 4, 5};
int Kmp(char* s, int n, char* t, int m) {
    int i = 0, j = 0;
    while(i < n) {
        if(j == -1 || s[i] == t[j]) {
            ++i; ++j;
            if(j == m) {
                return i - m + 1;
            }
        }
        else {
            j = next[j];
        }
    }
    return -1;
}


int main()
{
    //ababababadababaaababaa
    //ababaaababaa
    //ababcabcacbab
    //abc
    scanf("%s%s", s, t);
    int l1 = strlen(s);
    int l2 = strlen(t);
    printf("第一个匹配的位置%d\n", Kmp(s, l1, t, l2));
    return 0;
}


//next[i]: 0 -> i-1 最大的前缀和后缀匹配的长度 0 -> n-1
//S: a b a b b a b a b a d a b a b a a a b a b a a
//T: a b a b a a a b a b a a
//  -1 0 0 1 2 3 1 1 2 3 4 5
//从头选择next[j]个字符和从尾向前选择next[j]个字符是相等的
//
//1>, S串和T串前面这些字符是相等的
//2>, T串前面字符有next[j]前缀和后缀匹配的长度


16.二分图

这个算法的主要思想还是“递归”,通过递归腾出位置来让更多的人匹配,以达到匹配人数最大化

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int f[500+5];//女和哪个男配对 bool fr[505][505];//可用vector<int> d[505]代替 bool vis[505];//标记数组,表示这个人搜索过 int k,n,m;bool find(int x)//寻找x男生,返回:能否找到人去匹配 {for (int i = 1 ; i <= m ; i++){if (fr[x][i] && !vis[i]){vis[i] = true;//首先标记if (f[i] == -1)//如果该女生没有和别人匹配 {f[i] = x;//则和该男生匹配 return true;//表示匹配完成 }else if (find(f[i]))//如果该女生匹配的男生可以找到其他的女生匹配{f[i] = x;//递归的思想return true;}}}return false;}int main(){while (~scanf ("%d",&k) && k){scanf ("%d %d",&n,&m);memset(f,-1,sizeof(f));//初始化,表示没有和任何人配对memset(fr,false,sizeof(fr));//初始化 for (int i = 1 ; i <= k ; i++){int x,y;scanf ("%d %d",&x,&y);fr[x][y] = true;}int ans = 0;//表示已经配对的男生数 for (int i = 1 ; i <= n ; i++){memset(vis,false,sizeof(vis));if (find(i))ans++;}printf ("%d\n",ans);}return 0;}