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;                                                                           //方丈只好委委屈屈地当了师太的手下啦

}

 


 

 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;
}

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.模板一

模板整理(一)_i++模板整理(一)_结点_02
 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 }  
View Code

 2.模板二

模板整理(一)_i++模板整理(一)_结点_02
 1 typedef long long ll;
 2 ll mod_pow(ll x, ll n, ll mod)
 3 {
 4     ll res = 1;
 5     while(n > 0)
 6     {
 7         if(n & 1)
 8             res = res * x % mod;
 9         x = x * x % mod;
10         n >>= 1;
11     }
12     return res;
13 }
View Code

3.模板三

模板整理(一)_i++模板整理(一)_结点_02
1 ll mod_pow(ll x, ll n, ll mod)
2 {
3     if(n == 0)
4         return 1;
5     ll res = mod_pow(x * x % mod, n / 2, mod);
6     if(n & 1)
7         res = res * x % mod;
8     return res;
9 }
View Code
 
 

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++)  //之所以对t取与运算就是为了p[0]         if(n%p[i]==0)return 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. 二分查找
#include<iostream>
using namespace std;
int binary_search(int arr[], int n, int key)
{
           int left = 0;   //数组的首位置,即arr[0]处
           int right = n - 1;//数组的最后一个位置,即arr[n-1],数组大小为n

           //循环条件一定要注意
           while (left <= right)
          {
                    int mid = left + ((right - left) >> 1);//此处的mid的计算一定要放在while循环内部,否则mid无法正确更新;并且此处用移位代替除以2可以提高效率,而且可以防止溢出。
                    if (arr[mid] > key)//数组中间的位置得数大于要查找的数,那么我们就在中间数的左区间找
                   {
                             right = mid - 1;
                   }
                    else if (arr[mid] < key)//数组中间的位置得数小于要查找的数,那么我们就在中间数的右区间找
                   {
                             left = mid + 1;
                   }
                    else
                   {
                              return mid;//中间数刚好是要查找的数字。
                   }
          }

           //执行流如果走到此处说明没有找到要查找的数字。
           return -1;
}

  

 

 

lower_bound() 

函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置

举例如下:

一个数组number序列为:4,10,11,30,69,70,96,100.设要插入数字3,9,111.pos为要插入的位置的下标

pos = lower_bound( number, number + 8, 3) - number,pos = 0.即number数组的下标为0的位置。

pos = lower_bound( number, number + 8, 9) - number, pos = 1,即number数组的下标为1的位置(即10所在的位置)。

pos = lower_bound( number, number + 8, 111) - number, pos = 8,即number数组的下标为8的位置(但下标上限为7,所以返回最后一个元素的下一个元素)。

所以,要记住:函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置,且last的位置是越界的!!~

返回查找元素的第一个可安插位置,也就是“元素值>=查找值”的第一个元素的位置



7   栈和队列



8   贪心(这个好长以后有兴趣再慢慢看)
/*示例 
****哈夫曼编码****
请输入结点个数:8
输入这8个元素的权值(均为整形):
1:27
2:4
3:87
4:21
5:2
6:21
7:1
8:25

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
  unsigned int weight;  //用来存储各个结点的权值 
  unsigned int parent,LChild,RChild;  //指向双亲、孩子结点的指针 
} HTNode, *HuffmanTree;  //动态分配数组,存储哈夫曼树 

typedef char *HuffmanCode;  //动态分配数组,存储哈夫曼树

///选择两个parent为0,且weight最小的结点s1和s2 
void Select(HuffmanTree *ht,int n,int *s1,int *s2)
{
 int i,min;
 for(i=1; i<=n; i++)
 {
   if((*ht)[i].parent==0)
   {
       min=i;
       break;
   }
 }
 for(i=1; i<=n; i++)
 {
    if((*ht)[i].parent==0)
	{
      if((*ht)[i].weight<(*ht)[min].weight)
      min=i;
	}
 }
 *s1=min;

 for(i=1; i<=n; i++)
 {
    if((*ht)[i].parent==0 && i!=(*s1))
	{
      min=i;
      break;
	}
 }
 for(i=1; i<=n; i++)
 {
    if((*ht)[i].parent==0 && i!=(*s1))
	{
      if((*ht)[i].weight<(*ht)[min].weight) 
		  min=i;
	}
 }
 *s2=min;
}

///构造哈夫曼树ht,w存放已知n个权值 
void CrtHuffmanTree(HuffmanTree *ht,int *w,int n)
{
	 int m,i,s1,s2;
	 m=2*n-1;    //总共的结点数 
	 *ht=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
	 for(i=1; i<=n; i++)  //1-n号存放叶子结点,初始化 
	 {
		  (*ht)[i].weight=w[i];
		  (*ht)[i].LChild=0;
		  (*ht)[i].parent=0;
		  (*ht)[i].RChild=0;
	 }
	 for(i=n+1; i<=m; i++)   //非叶子结点的初始化
	 {
		  (*ht)[i].weight=0;
		  (*ht)[i].LChild=0;
		  (*ht)[i].parent=0;
		  (*ht)[i].RChild=0;
	 } 
	
	 printf("\n?哈夫曼树为: \n");
	
	 for(i=n+1; i<=m; i++)   //创建非叶子结点,建哈夫曼树
		 { /*在(*ht)[1]~(*ht)[i-1]的范围内选择两个parent为0且weight最小的结点,其序号分别赋值给s1、s2*/
		   Select(ht,i-1,&s1,&s2);
		  (*ht)[s1].parent=i;
		  (*ht)[s2].parent=i;
		  (*ht)[i].LChild=s1;
		  (*ht)[i].RChild=s2;
		  (*ht)[i].weight=(*ht)[s1].weight+(*ht)[s2].weight;
		  printf("%d (%d, %d)\n",(*ht)[i].weight,(*ht)[s1].weight,(*ht)[s2].weight);
	 	 }
	 printf("\n");
} 


//从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码
void CrtHuffmanCode(HuffmanTree *ht, HuffmanCode *hc, int n)
{
	 char *cd;   //定义的存放编码的空间
	 int a[100];
	 int i,start,p,w=0;
	 unsigned int c;
	 hc=(HuffmanCode *)malloc((n+1)*sizeof(char *));  //分配n个编码的头指针
	 cd=(char *)malloc(n*sizeof(char));  //分配求当前编码的工作空间
	 cd[n-1]='\0';  //从右向左逐位存放编码,首先存放编码结束符
	
	 for(i=1; i<=n; i++)  //求n个叶子结点对应的哈夫曼编码
	 {
		  a[i]=0;
		  start=n-1;  //起始指针位置在最右边
		  for(c=i,p=(*ht)[i].parent; p!=0; c=p,p=(*ht)[p].parent)  //从叶子到根结点求编码
		  {
			
			    if( (*ht)[p].LChild==c)
				{
					cd[--start]='1';  //左分支标1
					a[i]++;
				}
			    else 
				{
					cd[--start]='0';  //右分支标0
					a[i]++;
				}
		  }
		  hc[i]=(char *)malloc((n-start)*sizeof(char));  //为第i个编码分配空间
		  strcpy(hc[i],&cd[start]);    //将cd复制编码到hc
	 }
	 free(cd);
	 for(i=1; i<=n; i++)
	  printf(" 权值为%d的哈夫曼编码为:%s\n",(*ht)[i].weight,hc[i]);
 		for(i=1; i<=n; i++)
   			 w+=(*ht)[i].weight*a[i];
	 printf(" 带权路径为:%d\n",w);

}

int main()
{
	 HuffmanTree HT;
	 HuffmanCode HC;
	 int *w,i,n,wei;	
	 printf("**哈夫曼编码**\n" );
	 printf("请输入结点个数:" );
	 scanf("%d",&n);
	 w=(int *)malloc((n+1)*sizeof(int)); 
	 printf("\n输入这%d个元素的权值:\n",n); 
	
	 for(i=1; i<=n; i++)
	 { 
		  printf("%d: ",i); 
		  fflush(stdin);
		  scanf("%d",&wei);
		  w[i]=wei;
	 }
	 CrtHuffmanTree(&HT,w,n);
	 CrtHuffmanCode(&HT,&HC,n);
	 
	 system("pause");
	 return 0;
}

  



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 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);  //×max_element(dp+1,dp+n+1);
        }  
        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];
 //lower_bound()在last和first之间进行二分查找 前闭后开   返回第一个大于或等于val的位置  替换
        printf("%d\n",lower_bound(dp,dp+n,INF)-dp);   //如果没有则返回最后一个元素的位置
    } 

    return 0;
}   
 


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;  



 









 

永远渴望,大智若愚(stay hungry, stay foolish)