zoukankan      html  css  js  c++  java
  • ACM刷题之路(十六)Acm程序设计竞赛自制模板

    2020年2月更新:算法模板V2.1版

    下载地址


    前言

       本模板是我在备战省赛的时光中,把复习过的和新学的算法中比较常用的代码、思路,整合成了模板,供以后的ACM竞赛直接使用,因为时间匆忙、水平有限,若有不足之处,欢迎大佬提出。本版本为V1.0版,会不定期更新。

       以下所有代码都是我个人手敲,并且通过HDU、POJ等知名OJ上做题实践验证过的结果,是我两个月的成果。

        水平有限,代码比较累赘,以后有机会进一步减少少考的算法,增加比较常用的算法合集。

        希望能够抛砖引玉,如有意见,欢迎留言,或E-mail  ypacmzwz@163.com

    目录

     

    前言..................................................................................................................................................................... 1

    目录..................................................................................................................................................................... 1

    常用思路.............................................................................................................................................................. 1

    常见递推公式....................................................................................................................................................... 1

    函数库—algorithm................................................................................................................................................ 1

    函数库—cstring.................................................................................................................................................... 2

    函数库—cmath..................................................................................................................................................... 2

    快速幂................................................................................................................................................................. 3

    回文数................................................................................................................................................................. 3

    二分..................................................................................................................................................................... 3

    尺取法................................................................................................................................................................. 3

    辗转相除法.......................................................................................................................................................... 3

    水仙花数.............................................................................................................................................................. 4

    并查集................................................................................................................................................................. 4

    分割问题.............................................................................................................................................................. 4

    日期差公式.......................................................................................................................................................... 4

    组合大小计算....................................................................................................................................................... 4

    最大连续子序列和................................................................................................................................................ 5

    LIS(最长上升子序列)............................................................................................................................................. 5

    LCS(最长公共子序列)............................................................................................................................................ 6

    大数a+b............................................................................................................................................................... 6

    求第k个排列(阶乘法)........................................................................................................................................... 7

    最短路1. Dijkstra算法.......................................................................................................................................... 8

    最短路2. Floyd算法.............................................................................................................................................. 9

    最小生成树prim算法.......................................................................................................................................... 10

    凸包................................................................................................................................................................... 11

    深搜dfs.............................................................................................................................................................. 13

    广搜bfs.............................................................................................................................................................. 13

    母函数或多重背包.............................................................................................................................................. 13

    01背包............................................................................................................................................................... 14

    完全背包............................................................................................................................................................ 14

    多重背包............................................................................................................................................................ 14

    状压DP.............................................................................................................................................................. 15

    线段树................................................................................................................................................................ 16

    常用思路

    1.计算a到b之间有多少符合条件的数,可打表后二分找a和b的位置后相减

    例:

    scanf("%lld%lld", &n, &m);
    i = lower_bound(s1.begin(), s1.end(), n) - s1.begin();
    j = upper_bound(s1.begin(), s1.end(), m) - s1.begin();
    printf("%lld
    ", j - i);
    

    2.前缀和思想,不要直接暴力模拟。

    3.(x1,y1)(x1,y1)(x2,y2)(x2,y2)两点连线之间的整点数是gcd(|x1−x2|,|y1−y2|)+1

    4. 满足在[2,n]范围内有多少个数是非次方数(也就是不是x^k(k>1)这样的)

    答案:

    常见递推公式

    f(n)=4*f(n-2)-f(n-4)

    f(n)=f(n-1)+6×(i-1)

    f(n)=f(n-4)+f(n-2)+f(n-1)(n>4)

    f[i]=f[i-1]+f[i-2]*4;

    f(n)=f(n-2)+f(n-1),n>3。

    F[i] = F[i-1] + 2 * F[i-2];

    a[n][m] = a[n-1][m] +a[n][m-1]

    函数库—algorithm

            //————————algorithm————————
    	int a[] = { 1, 3, 5, 7, 9, 11, 13 };
    	lower_bound(a, a + 7, 7);//返回第一个大于等于7的地址
    	upper_bound(a, a + 7, 7);//返回第一个小于等于7的地址
    	binary_search(a, a + 7, 8);//若a到a+7有8,返回true 否则返回false
    	reverse(a, a + 7);//反转a到a+7的元素
    	fill(a, a + 7, 4);//填充函数,把a到a+7全部填充为4
    	int b[11] = { 1, 2, 3, 4 };
    	copy_backward(a, a + 7, b + 7);//把a数组复制到b,首地址,尾地址,复制后数组的尾地址
    	next_permutation(b, b + 4);//b数组的下一个排列
    	prev_permutation(b, b + 4);//b数组的上一个排列
    	replace(b, b + 4, 3, 5);//把b到b+4中所有3替换成5
    	stable_sort(a, a + 7, cmp);//按照cmp规则稳定排序a到a+7
    	unique(a, a + 7);//去重,返回去重后数组的尾地址
    	printf("%d
    ", *max_element(a, a + 6));//返回序列a到a+6的最大元素地址
    	//————————algorithm————————

    函数库—cstring

    //————————cstring————————
            char a[200] = "hello world";
    	char b[] = "hello acm";
    	cout << a << endl << b << endl;
    	memset(a, 0, sizeof(a));//初始化 只能0 -1
    	int len = strlen(a);//返回a的长度  到''就算结束
    	strcpy(a, b);//把b赋值给a 覆盖掉
    	memcpy(a, b, 8);//把b赋值给a 覆盖掉8个长度
    	strcat(a, b);//把b连接到a后面
    	strncat(a, b, 3);//把b的最多3个字符连接到a后面
    	strcmp(a, b);//a>b 返回正数,a<b返回负数,一样返回0
    	strncmp(a, b, 7);//比较a和b的前7位字符 返回规则同上
    	int xiabiao = strchr(a, 'l') - a;//返回a中找字符l出现的首地址 没有返回NULL
    	int xiabiao2 = (char*)memchr(a, 'l', 7) - a;//返回a的前7个字符中找字符l出现的首地址 没有返回NULL
    	strspn(a, b);//比较a和b 从第一位开始比较返回从头数相等的长度
    	strstr(a, b)-a;//返回b在a首次出现的地址 
    //————————cstring————————
    

     函数库—cmath

            int a = 1, b = -2, c = 3, d = 4;
    	double e = 1.1, f = 8.36, g = 2.2, h =3.4;
    	/*————————<cmath>  部分————————*/
    	e = sqrt(f);//平方根函数 返回根号f
    	e = cbrt(f);//立方根函数 返回三次根号f
    	e = pow(f, g); //幂函数 返回f的g次方
    	e = floor(f);//向下取整 返回f的向下取整的整数
    	e = ceil(f);//向上取整 返回f的向上取整的整数
    	a = abs(b);//int类型 返回b的绝对值
    	e = fabs(f);//double类型 返回f的绝对值
    	e = fmod(f, g);//double类型 返回f除以g的余数
    	e = modf(2.36, &f);//把2.36的整数部分赋值给f(有&) 把小数返回给e
    	e = frexp(1024.0, &a);//把1024.8转化为0.5*2^11;0.5返回 11赋值给a,返回的小数范围[0.5,1)
    	e = ldexp(1.0, 3);//返回1.0 *(2^3) 
    	e = exp(3);//返回e的3次方     exp(1)就是e的值  acos(-1)就是pai的值
    	f = log(666.0);//返回log e (666.0)   以e为底数
    	f = log10(666.0);//返回log 10 (666.0)   以10为底数
    	f = log10(8) / log10(2);// 计算log 2 (8) 运用换底公式
    	f = acos(-1);//返回以弧度表示的 -1 的反余弦
    	f = asin(-1);//返回以弧度表示的 -1 的反正弦
    	f = atan(-1);//返回以弧度表示的 -1 的反正切
    	f = atan2(1, 2); //返回以弧度表示的 1/2 的反正切。1和2的值的符号决定了正确的象限。
    	f = cos(1.1);//返回弧度为1.1的余弦
    	f = sin(1.1);//返回弧度为1.1的正弦
    	f = tan(1.1);//返回弧度为1.1的正切
    	f = cosh(1.1);//返回弧度为1.1的双曲余弦
    	f = sinh(1.1);//返回弧度为1.1的双曲正弦
    	f = tanh(1.1);//返回弧度为1.1的双曲正切
    	f = hypot(3, 4);//返回以3和4为直角边的三角形斜边长
    	/*————————<cmath>  部分————————*/
    

     快速幂

    int poww(int n, int m)
    {
    	int ans = 1;
    	while (m > 0)
    	{
    		if (m & 1) ans *= n;
    		n *= n;
    		m /= 2;
    	}
    	return ans;
    }
    

    回文数

    bool huiwen(int n)
    {//判断一个数字是否回文
    	int temp = n, t = 0;
    	while (temp)
    	{
    		t = t * 10 + temp % 10;
    		temp /= 10;
    	}
    	return t == n;
    }
    

    尺取法

            int s=0,e=0,sum=a[0];
    	int chang=0,min=1000010;
    	while(e<n)
    	{//s到e的一把尺子
    		if(sum<m)//如果sum小于预想结果
    		{
    			e++;//尺子后面边长
    			sum+=a[e];
    		}
    		else if(sum>=m)//若达到结果
    		{
    			if(e-s+1<min) 
    			{
    				min=e-s+1;//记录最小值
    			}
    			sum-=a[s];
    			s++;   //尺子前面缩短
    		}
    		
    	}
    	if(min!=1000010) //若有满足答案
    		printf("%d
    ",min);
    	else 
    		printf("0
    ");
    
    

    二分

    double s=开始,e=结尾,mid;
    	while(1)
    	{
    		mid=(s+e)/2.0;
    		if(满足条件)
    		{//或者while里面写(左<=右)
    			break;
    		}
    		else if(结果小于预想答案)
    		{
    			s=mid(按照情况+1或者不加);
    		}
    		else
    		{
    			e=mid(按照情况-1或者不加);
    		}
    	}
    	printf("%.4lf
    ",mid);
    

    辗转相除法

    int gcd(int x, int y)  //547ms
    {
    	int z;
    	while (y > 0)
    	{
    		z = x%y;
    		x = y;
    		y = z;
    	}
    	return x;
    }
    int gcd2(int a, int b)  //544ms
    {
    	return b == 0 ? a : gcd(b, a%b);
    }

    水仙花数

    三位的水仙花数共有4个:153,370,371,407;
    四位的四叶玫瑰数共有3个:1634,8208,9474;
    五位的五角星数共有3个:54748,92727,93084;
    六位的六合数只有1个:548834;
    七位的北斗七星数共有4个:1741725,4210818,9800817,9926315;
    八位的八仙数共有3个:24678050,24678051,88593477
    

    并查集

    int a[10005];
    int find(int x)//查找自己的老大
    {
    	if (a[x] == x) return x;
    	else return a[x] = find(a[x]);
    }
    void mix(int x, int y)//合并
    {
    	if (find(x) != find(y))
    	{
    		a[find(x)] = find(y);
    	}
    }
    主函数:
    1.	for (i = 1; i <= 10000; i++)//初始化自己是自己的老大
    		a[i] = i;
    2. for (i = 1; i <= max; i++)//合并后需路径压缩
    	{
    		find(i);
    	}
    3. if (find(e) == find(r))			puts("Y");
    		else	puts("N");//如果老大一样,就是一个集合
    

    日期差公式

    int day_diff(int year1, int month1, int day1, int year2, int month2, int day2)
    {//年月日  年月日  日期差公式 求相差几天
    	int y2, m2, d2, y1, m1, d1;
    	m1 = (month1 + 9) % 12;
    	y1 = year1 - m1 / 10;
    	d1 = 365 * y1 + y1 / 4 - y1 / 100 + y1 / 400 + (m1 * 306 + 5) / 10 + (day1 - 1);
    
    	m2 = (month2 + 9) % 12;
    	y2 = year2 - m2 / 10;
    	d2 = 365 * y2 + y2 / 4 - y2 / 100 + y2 / 400 + (m2 * 306 + 5) / 10 + (day2 - 1);
    	return (d2 - d1);
    }
    

    组合大小计算

    #include<iostream>
    #include<time.h>
    using namespace std;
    double zuhe(int n, int m)
    {
    	double sum = 1;
    	int i;
    	for (i = 0; i < n; i++)
    	{
    		if (n - i>m) sum *= n - i;
    		if (n - m - i > 1) sum /= n - m - i;//顺带抵消,防止爆double
    	}
    	return sum;
    }
    
    double zuhe2(int n,int r)
    {
    	double s = 1;
    	int i;
    	for (i = 1; i <= r; i++)
    		s = s*(n - i + 1) / i;
    	return s;
    }
    
    int main()
    {
    	int n, m;
    	while (cin >> n >> m)
    	{
    		cout << zuhe(n, m) << endl;//506 ms
    		cout << zuhe2(n, m) << endl;//866 ms
    	}
    	return 0;
    }
    

    分割问题

    直线分割平面: 任意两条直线都要相交,且任意三条直线不能有同一个交点
    即f(n) = f(n - 1) + n     或  f(n) = 1/2 * (n*n + n)  + 1
    平面分空间: F(n) = (n * n * n + 5 * n + 6) / 6;
    另: 平方项的通项公式 1^2+2^2+3^2+……+n^2=n(n+1)(2n+1)/6
    立方差公式: n^3-(n-1)^3 = =2*n^2+(n-1)^2-n
    

    最大连续子序列和

    int a[100003];
    int longziliehe(int n)
    {
    	int maxsum = 0;
    	int thissum = 0;
    	for (int i = 0; i < n; i++)
    	{
    		scanf("%d", &a[i]);
    		thissum += a[i];
    		if (thissum >= maxsum) maxsum = thissum;
    		if (thissum < 0) thissum = 0;
    	}
    	return maxsum;
    }
    

    LIS(最长上升子序列)

    1.o(n*logn)
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int a[500020];//初始数组
    int dp[500020];//最后的LIS数组
    int lis(int n)
    {
    	memset(dp, 0, sizeof(dp));
    	dp[1] = a[1];
    	int len = 1;
    	for (int i = 2; i <= n; i++)
    	{
    		int wei = lower_bound(dp + 1, dp + 1 + len, a[i]) - dp;
    		if (wei > len)
    		{
    			len++;
    			dp[wei] = a[i];
    		}
    		else
    		{
    			dp[wei] = a[i];
    		}
    	}
    	return len;
    }
    int main()
    {
    	int n, i, j;
    cout << "请输入需要求LIS的数组长度" << endl;
    cin >> n;
    cout << "数组长度输入完毕,请依次输入数组元素" << endl;
    	for (i = 1; i <= n; i++)
    	{
    		scanf("%d", &a[i]);
    	}
    	int max = lis(n);
    	cout << "该数组的LIS数组长度为" << max << endl;
    	cout << "该数组的最优LIS数组为" << endl;
    	for (i = 1; i <= max; i++)
    	{
    		cout << dp[i] << " ";
    	}
    	cout << endl;
    	return 0;
    }
    
    2.	o(n^2)
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int a[500020];
    int dp[500020];
    int lis(int n)
    {
    	memset(dp, 0, sizeof(dp));
    	dp[1] = 1;
    	int max = 1;
    	for (int i = 2; i <= n; i++)
    	{
    		for (int j = 1; j < i; j++)
    		{
    			if (a[j]<a[i] && dp[j] + 1> dp[i])
    			{
    				dp[i] = dp[j] + 1;
    				if (dp[i] > max) max = dp[i];
    			}
    		}
    	}
    	return max;
    }
    int main()
    {
    	int n, i, j, k;
    	while (cin >> n)
    	{
    		for (i = 1; i <= n; i++)
    		{
    			scanf("%d", &a[i]);
    		}
    		int max = lis(n);
    		cout << max << endl;
    	}
    	return 0;
    }
    

    LCS(最长公共子序列)

    1.	滚动数组
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int map[2][10005];
    string str1, str2;
    int len1, len2;
    void LCS(int x, int y)
    {
    	for (int i = 0; i <= x; i++)
    	{
    		for (int j = 0; j <= y + 1; j++)
    		{
    			if (i == 0 || j == 0) 
    { map[i % 2][j] = 0; continue; }
    			if (str1[i - 1] == str2[j - 1])
    			{
    			map[i % 2][j] = map[(i - 1) % 2][j - 1] + 1;
    			}
    			else
    			{
    map[i % 2][j] = max(map[(i - 1) % 2][j], map[i % 2][j - 1]);
    			}
    		}
    	}
    }
    int main()
    {
    	while (cin >> str1 >> str2)
    	{
    		len1 = str1.length();
    		len2 = str2.length();
    		LCS(len1, len2);
    		printf("%d
    ", map[len1 % 2][len2]);
    	}
    	return 0;
    }
    2.o(n+m)
    #include<cstdio>
    #include<string>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int map[10005][10005];
    string str1, str2;
    int len1, len2;
    int max(int x, int y)
    {
    	if (x > y) return x;
    	return y;
    }
    
    
    int main()
    {
    	while (cin >> str1 >> str2)
    	{
    		len1 = str1.length() - 1;
    		len2 = str2.length() - 1;
    		for (int i = 0; i <= len1 + 1; i++)
    		{
    			for (int j = 0; j <= len2 + 1; j++)
    			{
    				if (i == 0 || j == 0)
    				{ 
    					map[i][j] = 0;
    					continue; 
    				}
    				if (str1[i - 1] == str2[j - 1])
    				{
    			map[i][j] = map[i - 1][j - 1] + 1;
    				}
    				else
    				{
    map[i][j] = max(map[i - 1][j], map[i][j - 1]);
    				}
    			}
    		}
    	printf("%d
    ", map[len1 + 1][len2 + 1]);
    	}
    	return 0;
    }
    

    大数a+b

    #include<iostream>
    #include<string.h>
    using namespace std;
    int i, a[1000], b[1000];
    char s1[1000], s2[1000];
    int sum(char *s1, char *s2)
    {
    int len, len1 = strlen(s1), len2 = strlen(s2), t;
    memset(a, 0, sizeof(a)), memset(b, 0, sizeof(b));
    	for (i = 0; i<len1; i++)   
     //字符转换成数字,倒序存放,方便求和
    		a[i] = s1[len1 - 1 - i] - '0';
    	for (i = 0; i<len2; i++)
    		b[i] = s2[len2 - 1 - i] - '0';
    	len = len1>len2 ? len1 : len2;
    	for (t = i = 0; i <= len; i++)
    	{
    		a[i] += (b[i] + t);   
     //把数组b里的数字加到a数组里  
    		t = a[i] / 10;    //t为进位数  
    		a[i] %= 10;
    	}
    	return a[len] == 0 ? len - 1 : len;//判断求和之后的长度的len还是len-1   
    }
    int main()
    {
    	int i;
    	while (~scanf("%s%s",s1,s2))
    	{
    		int len = sum(s1, s2);
    		for (i = len; i >= 0; i--)
    		{
    			printf("%d", a[i]);
    		}
    		puts("");
    	}
    	return 0;
    }
    
    import java.math.BigInteger;
    import java.util.Scanner;
    public class Main 
    {
    	public static void main(String args[])
    	{
    		Scanner in = new Scanner(System.in);
    		while(in.hasNext())
    		{
    			BigInteger a= in.nextBigInteger();
    			BigInteger b= in.nextBigInteger();
    			System.out.println(a + " + " + b + " = " + a.add(b));
    		}
    	}
    }
    

    求第k个排列(阶乘法)

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    vector<int>a;
    
    
    
    long long jc(int x)//阶乘的递归算法
    {
    	if (x < 2) return 1;
    	return x*jc(x - 1);
    }
    
    
    
    
    void shengcheng(int n)//生成1到n的排列,在a数组中
    {
    	a.clear();
    	for (int i = 0; i < n; i++)
    		a.push_back(i + 1);
    }
    
    
    
    int maxjc(int n,int k)
    //求小于等于k的最大的阶乘数 n为排列最大阶乘
    {
    	for (int i = n; i>0; i--)
    	{
    		if (jc(i) <= k) return i;
    	}
    	return -1;
    }
    
    void makepailie(int n,int k)//求第k个排列
    {
    	int kk = k;//用于后面输出
    	k--;//这里需要减去一
    	int t = maxjc(n, k);
    //求出小于等于k的最大的阶乘数
    	queue<int>q;
    	for (int i = 1; i < n - t; i++)
    	{
    		q.push(i);
    		a.erase(a.begin());
    	}
    	while (t>=0)
    	{
    		int zheng = k / jc(t);
    		int yu = k % jc(t);
    		q.push(a[zheng]);
    		a.erase(a.begin() + zheng);
    		k %= jc(t);
    		t--;
    	}
    	cout << " 1 到";
    	printf("%2d ", n);//控制格式
    	cout << "的第";
    	printf(" %2d ", kk);//控制格式
    		cout<< "个排列为 : ";
    	while (!q.empty())
    	{
    		cout << "  " << q.front();
    		q.pop();
    	}
    	cout << endl;
    }
    
    int main()
    {
    	int n, i, k;
    	//------------------------------------------------
    	//功能1: 输入n ,输出 1到n 的全排列
    	while (cin >> n)
    	{
    		for (i = 1; i <= jc(n); i++)
    		{
    			shengcheng(n);
    			makepailie(n, i);
    		}
    	}
    	//------------------------------------------------
    	//功能2: 实现1到n 的第K个排列 
    	while (cin >> n >> k)
    	{
    		shengcheng(n);
    		makepailie(n, k);
    	}
    	//------------------------------------------------
    	return 0;
    }
    

    最短路1. Dijkstra算法

    hdu 2544 最短路贪心手法  先把除1以外的距离初始化为无穷大
    先从1开始 利用1可以连接到其他顶点的边 更新距离数组d[],把1确定下来  1到1距离为0已固定
    在取非固定点的最小值 同样利用这个点可以连接到其他顶点的边 更新距离数组d[]...... 以此类推
    这样循环n次  即可求出1到n的最短路径  如果不连通  当某一个find函数返回-1  即可判断该图不连通
    #include<iostream>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int n, m;
    bool b[102];//判断最短距离是否确定, 已确定为true 否则为假
    int d[102];//保存1到各个节点的距离 若b数组相应值为true 则必为最短路 否则为临时最短路
    #define inf 10000000
    int find()//找到所有距离的最小值
    {
    	int i, j = -1, min = inf;
    	for (i = 1; i <= n; i++)
    	{
    		if (b[i]==false && d[i] < min)
    		{
    			j = i;
    			min = d[i];
    		}
    	}
    	return j;
    }
    int main()
    {
    	int i, j;
    	int q, w, e;
    	while (cin >> n >> m)
    //输入点数n  和 边数 m
    	{
    		vector<pair<int, int> >v[102];
    //用vector + pair 模拟二维数组 即三个变量的对应关系
    		if (n == 0 && m == 0) break;
    		while (m--)
    		{
    			pair<int, int>p;//pair为辅助容器
    			scanf("%d%d%d", &q, &w, &e);
    			p.first = w;
    			p.second = e;
    			v[q].push_back(p);
    // q 到 w 的 边长 为 e
    			p.first = q;
    			v[w].push_back(p);
    // w 到 q 的 边长 为 e
    		}
    		fill(b, b + 102, false); 
    //初始化函数  节点全部初始化为未访问
    		fill(d, d + 102, inf);
    //距离全部初始化为无穷大
    		d[1] = 0; // 1到1的距离为0
    		for (i = 1; i <= n; i++)
    		{
    			j = find();
    			b[j] = true;
    for (vector<pair<int, int> >::iterator it = v[j].begin(); it != v[j].end(); it++)
    {
    //此循环为对j到其他结点遍历  更新最短边
    if (d[it->first] > d[j] + it->second)//如果可以通过中间点使路变的更短
    {
    d[it->first] = d[j] + it->second;
    }
    	}
    }
    		cout << d[n] << endl;
    //输出1到n的最短距离 
    	}
    	return 0;
    }
    
    测试数据
    9 15
    1 2 6
    1 3 3
    1 4 1
    2 3 2
    3 4 2
    4 5 6
    2 5 1
    4 6 10
    5 6 4
    5 7 3
    5 9 6
    5 8 2
    8 9 3
    6 7 2
    7 9 4
    答案:11
    

    最短路2. Floyd算法

    hdu 2544 最短路 Floyd算法 总结
    
    状态  :Accepted  31MS  1848K
    基本思想:点A到点B无非是A直接到B 和 A通过若干个点再到B 这两种可能
    对于每一个点 都当一遍中间点 判断a[j][i] + a[i][k] < a[j][k]
    最后的a数组即可表示最短路径  a[i][j]代表i到j的最短路径 
    
    
    时间复杂度为o(n^3) 
    
    
    #include<iostream>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int n, m;
    int a[105][105];
    //a数组直接保存每个点之间的最短距离
    #define inf 10000000
    
    
    
    
    
    
    
    
    
    
    
    
    
    int main()
    {
    	int i, j, k;
    	int q, w, e;
    	while (cin >> n >> m)
    //输入点数n  和 边数 m
    	{
    		if (n == 0 && m == 0) break;
    		for (i = 0; i < 105; i++)
     //全部初始化为无穷大
    		{
    			for (j = 0; j < 105; j++)
    			{
    				a[i][j] = inf;
    			}
    		}
    		while (m--)
    		{
    			scanf("%d%d%d", &q, &w, &e);
    			a[q][w] = e;//q到w的距离为e
    			a[w][q] = e;//w到q的距离为e
    		}
    //Floyd算法 o(n^3) 暴力循环判断点i是否可以作为中间点
    		for (i = 1; i <= n; i++)
    		{
    			for (j = 1; j <= n; j++)
    			{
    				for (k = 1; k <= n; k++)
    				{
    if (a[j][i] + a[i][k] < a[j][k])
    //如果i当中间点可以缩短路径长度
    {
    	a[j][k] = a[j][i] + a[i][k];
    }
    				}
    			}
    		}
    		cout << a[1][n] << endl;
    //输出1到n的最短距离 
    	}
    	return 0;
    }
    

    最小生成树prim算法

    /*
    算法思路:
    首先就是从图中的一个起点a开始,把a加入U集合,然后,寻找从与a有关联的边中,
    权重最小的那条边并且该边的终点b在顶点集合:(V-U)中,我们也把b加入到集合U中,
    并且输出边(a,b)的信息,这样我们的集合U就有:{a,b},
    然后,我们寻找与a关联和b关联的边中,权重最小的那条边并且该边的终点在集合:
    (V-U)中,我们把c加入到集合U中,并且输出对应的那条边的信息,这样我们的集合U就有:
    {a,b,c}这三个元素了,一次类推,直到所有顶点都加入到了集合U。
    2018/12/6   最小生成树   Prim算法
    */
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int inf = 10000000;
    const int N = 1002;
    int G[N][N];//G数组 保存 边长
    int d[N];
    //d数组  保存所有点离树的最短距离
    int n, m;
    bool vis[N];
     // vis数组 保存 判断这个点是否放入树中 
    int prim()
    {
    	fill(d, d + N, inf);//初始化无穷大
    	d[1] = 0;
    	int ans = 0;//初始化答案为0
    
    
    
    
    
    	for (int i = 1; i <= n; i++)
    //i层循环 找所有连通到的边中最短的
    	{
    		int u = -1;//找单次的最短边
    		int min = inf;
    		for (int j = 1; j <= n; j++)
    		{
    			if (vis[j] == false && d[j]<min)//遍历求出 没有进入树的节点 离树的最短距离
    			{
    				u = j;
    				min = d[j];
    			}
    		}
    		if (u == -1)//如果所有边都无穷大,说明不连通,就返回错误
    			return -1;
    		vis[u] = true;//该点入树
    		ans += d[u];//距离总和加上U点离树的距离
    		for (int v = 1; v <= n; v++)
    		{//这里是更新没有入树的节点离树的最短距离
    			if (vis[v] == false && G[u][v] != inf&&G[u][v]<d[v])
    				d[v] = G[u][v];
    		}
    	}
    	return ans;
    }
    int main()
    {
    	int u, v, c;
    	cin >> n >> m;
    	fill(G[0], G[0] + N*N, inf);
    	for (int i = 1; i <= m; i++)
    	{
    		cin >> u >> v >> c;
    		G[u][v] = G[v][u] = c;
    	}
    	int ans = prim();
    	if (ans == -1)//如果不连通
    		cout << "-1" << endl;
    	else
    		cout << ans << endl;
    	return 0;
    }
    

    凸包

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define PI 3.1415926535
    using namespace std;
    struct node
    {
    	int x, y;
    };
    node vex[1000];//存入的所有的点
    node stackk[1000];//凸包中所有的点
    int xx, yy;
    bool cmp1(node a, node b)//排序找第一个点
    {
    	if (a.y == b.y)
    		return a.x<b.x;
    	else
    		return a.y<b.y;
    }
    int cross(node a, node b, node c)//计算叉积
    {
    	return (b.x - a.x)*(c.y - a.y) - (c.x - a.x)*(b.y - a.y);
    }
    double dis(node a, node b)//计算距离
    {
    	return sqrt((a.x - b.x)*(a.x - b.x)*1.0 + (a.y - b.y)*(a.y - b.y));
    }
    bool cmp2(node a, node b)//极角排序另一种方法,速度快
    {
    	if (atan2(a.y - yy, a.x - xx) != atan2(b.y - yy, b.x - xx))
    		return (atan2(a.y - yy, a.x - xx))<(atan2(b.y - yy, b.x - xx));
    	return a.x<b.x;
    }
    bool cmp(node a, node b)//极角排序
    {
    	int m = cross(vex[0], a, b);
    	if (m>0)
    		return 1;
    	else if (m == 0 && dis(vex[0], a) - dis(vex[0], b) <= 0)
    		return 1;
    	else return 0;
    }
    int main()
    {
    	int t, L;
    	while (~scanf("%d", &t), t)//总共t个点
    	{
    		int i;
    		for (i = 0; i<t; i++)
    		{
    			scanf("%d%d", &vex[i].x, &vex[i].y);//输入每个点的x和y坐标
    		}
    		if (t == 1)//如果只有一个点 凸包周长为0
    			printf("%.2f
    ", 0.00);
    		else if (t == 2)
    			printf("%.2f
    ", dis(vex[0], vex[1]));//如果只有两个点 周长是两点距离
    		else
    		{
    			memset(stackk, 0, sizeof(stackk));
    			sort(vex, vex + t, cmp1);//排序找第一个点
    			stackk[0] = vex[0];//用数组模拟栈
    			xx = stackk[0].x;
    			yy = stackk[0].y;
    			sort(vex + 1, vex + t, cmp2);//极角排序
    			stackk[1] = vex[1];//将凸包中的第两个点存入凸包的结构体中
    			int top = 1;//最后凸包中拥有点的个数
    			for (i = 2; i<t; i++)
    			{
    				while (i >= 1 && cross(stackk[top - 1], stackk[top], vex[i])<0)
    					//i>=1防止越界  如果在右边 把栈顶元素出栈
    					top--;
    				stackk[++top] = vex[i];//控制<0或<=0可以控制重点,共线的,具体视题目而定。
    			}
    			double s = 0;
    			for (i = 1; i <= top; i++)   //计算凸包的周长
    				s += dis(stackk[i - 1], stackk[i]);
    			s += dis(stackk[top], vex[0]);//最后一个点和第一个点之间的距离
    			printf("%.2lf
    ", s);
    		}
    	}
    }
    

    深搜dfs

    void dfs(ll shu, int fei0, int changdu)//在搜索时需变化的量
    {
    	//满足条件返回
    	//越界返回 
    	//明显不符合条件返回 如距离奇偶性
    	//已经找到目标  返回
    	for (int i = 1; i <= 9; i++)
    	{
    		dfs(shu * 10 + i, fei0 + 1, changdu + 1);//继续深搜下去
    	}
    }
    

    广搜bfs

    void bfs(int x)
    {
    	queue<int>q1;//1.先创建队列
    	q1.push(x);//2.放入初始条件
    	while (!q1.empty())//3.开始循环广搜
    	{
    		int t = q1.front();
    		q1.pop();//4.顶部删掉
    		for (vector<int>::iterator it = v[t].begin(); it != v[t].end(); it++)
    		{//5.把顶部能牵连到的元素压入队列
    			if (a[*it] == false)
    				q1.push(*it);
    		}
    	}//6.全部循环结束后,如果需要返回值就返回
    }
    

    母函数或多重背包

    #include<iostream>
    #include<cstring>
    using namespace std;
    struct node
    {
    	int fen, num;
    };
    int dp[41];
    int main()
    {
    	int T;
    	scanf("%d", &T);
    	while (T--)
    	{
    		node a[10];
    		memset(dp, 0, sizeof(dp));
    		int n,  m;
    		scanf("%d%d", &m, &n);
    		int i, j, w;
    		for (i = 0; i < n; i++)
    			scanf("%d%d", &a[i].fen, &a[i].num);//输入每种物品的价值 和 物品数量
    		dp[0] = 1;//初始化dp数组
    		for (i = 0; i < n; i++)//枚举物品种类
    		{
    			for (j = m; j >= a[i].fen; j--)//枚举背包容量,01背包,要从大到小推,不然会重复加
    			{
    				for (w = 1; w <= a[i].num; w++)
    		//枚举这个物品的个数,如果当前枚举到的价值能放入此物品的话,就加上之前的价值
    				{
    					if (j >= a[i].fen*w)
    		//a[i].fen*w是w个物品能产生的价值,然后j-a[i].fen*w就是去掉a[i].fen*w的价值,加起来递推
    					{
    						dp[j] += dp[j - a[i].fen*w];
    					}
    					else break;
    				}
    			}
    		}
    		printf("%d
    ", dp[m]);//m个物品的时候所能产生的价值
    	}
    	return 0;
    }
    

    01背包

    for (i = 1; i <= n; i++)
    {
    	for (j = v; j >= a[i].cost; j--)
    	{
    		if (f[j] < f[j - a[i].cost] + a[i].val)
    			f[j] = f[j - a[i].cost] + a[i].val;
    	}
    }
    

    完全背包

    for (int i = 1; i <= n; i++)
    {
    	for (int j = w[i]; j <= v; j++)
    	{
    		dp[j] = min(dp[j], dp[j - w[i]] + p[i]);
    	}
    }
    

    多重背包

    for (int i = 0; i<n; i++)
    {      
    	for (int k = 0; k<num[i]; k++)
    	{            
    		for (int j = cc; j >= v[i]; j--)
    		{   
    			dp[j] = max(dp[j], dp[j - v[i]] + v[i]);
    		}
    	}
    }
    

    状压DP

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <stack>
    #include <algorithm>
    using namespace std;
    题意:有n门课,每门课有截止时间和完成所需的时间,如果超过规定时间完成,每超过一天就会扣1分,问怎样安排做作业的顺序才能使得所扣的分最小
    思路:因为最多只有15门课程,可以使用二进制来表示所有完成的状况
    例如5,二进制位101,代表第一门和第三门完成了,第二门没有完成,那么我们可以枚举1~1<<n便可以得出所有的状态
    然后对于每一门而言,其状态是t = 1<<i,我们看这门在现在的状态s下是不是完成,可以通过判断s&t是否为1来得到
    当得出t属于s状态的时候,我们便可以进行DP了,在DP的时候要记录路径,方便之后的输出
    const int inf = 1 << 30;
    struct node
    {
    	string name;
    	int dead, cost;
    } a[50];
    struct kode
    {
    	int time, score, pre, now;
    } dp[1 << 15];
    int main()
    {
    	int t, i, j, s, n, end;
    	cin >> t;
    	while (t--)
    	{
    		memset(dp, 0, sizeof(dp));
    		cin >> n;
    		for (i = 0; i<n; i++)
    			cin >> a[i].name >> a[i].dead >> a[i].cost;//每门课的名字、最晚完成时间、花费时间
    		end = 1 << n;//n-1指的是每门课都完成的情况
    		for (s = 1; s<end; s++)//暴力遍历
    		{
    			dp[s].score = inf;//先初始化为无穷大    是最少扣的分数
    			for (i = n - 1; i >= 0; i--)
    			{
    				int tem = 1 << i;//右往左数第i+1位数
    				if (s & tem)//如果右往左数第i+1位数为1   即i+1事件已经完成
    				{
    					int past = s - tem;//不可能为负 past代表当前事件发生之前的状态
    					int st = dp[past].time + a[i].cost - a[i].dead;
    //转移方程 要花的时间=前面要的时间+当前花的时间-最晚时间
    					if (st<0)//如果当前不会被扣分 则等于0
    						st = 0;
    					if (st + dp[past].score<dp[s].score)//更新取最小值
    					{
    						dp[s].score = st + dp[past].score;//当前要扣的分
    						dp[s].now = i;//当前dp最后执行的事件位置
    						dp[s].pre = past;//执行前的二进制状态
    						dp[s].time = dp[past].time + a[i].cost;
    					}
    				}
    			}
    		}
    		stack<int> S;
    		int tem = end - 1;
    		cout << dp[tem].score << endl;//输出总的最少扣分
    		while (tem)
    		{
    			S.push(dp[tem].now);//最后做的事件下表 入栈
    			tem = dp[tem].pre;//转到前面的事件
    		}
    		while (!S.empty())
    		{
    			cout << a[S.top()].name << endl;
    			S.pop();
    		}
    	}
    	return 0;
    }
    

    线段树

    #include<iostream>
    #include<cstring>
    using namespace std;
    int n, p, a, b, m, x, y, ans;
    struct node
    {
    	int l, r, w, f;
    }tree[400001];
    inline void build(int k, int ll, int rr)//建树 
    {
    	tree[k].l = ll, tree[k].r = rr;
    	if (tree[k].l == tree[k].r)
    	{
    		scanf("%d", &tree[k].w);
    		return;
    	}
    	int m = (ll + rr) / 2;
    	build(k * 2, ll, m);
    	build(k * 2 + 1, m + 1, rr);
    	tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
    }
    inline void down(int k)//标记下传 
    {
    	tree[k * 2].f += tree[k].f;
    	tree[k * 2 + 1].f += tree[k].f;
    	tree[k * 2].w += tree[k].f*(tree[k * 2].r - tree[k * 2].l + 1);
    	tree[k * 2 + 1].w += tree[k].f*(tree[k * 2 + 1].r - tree[k * 2 + 1].l + 1);
    	tree[k].f = 0;
    }
    inline void ask_point(int k)//单点查询
    {
    	if (tree[k].l == tree[k].r)
    	{
    		ans = tree[k].w;
    		return;
    	}
    	if (tree[k].f) down(k);
    	int m = (tree[k].l + tree[k].r) / 2;
    	if (x <= m) ask_point(k * 2);
    	else ask_point(k * 2 + 1);
    }
    inline void change_point(int k)//单点修改 
    {
    	if (tree[k].l == tree[k].r)
    	{
    		tree[k].w += y;
    		return;
    	}
    	if (tree[k].f) down(k);
    	int m = (tree[k].l + tree[k].r) / 2;
    	if (x <= m) change_point(k * 2);
    	else change_point(k * 2 + 1);
    	tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
    }
    inline void ask_interval(int k)//区间查询 
    {
    	if (tree[k].l >= a&&tree[k].r <= b)
    	{
    		ans += tree[k].w;
    		return;
    	}
    	if (tree[k].f) down(k);
    	int m = (tree[k].l + tree[k].r) / 2;
    	if (a <= m) ask_interval(k * 2);
    	if (b>m) ask_interval(k * 2 + 1);
    }
    inline void change_interval(int k)//区间修改 
    {
    	if (tree[k].l >= a&&tree[k].r <= b)
    	{
    		tree[k].w += (tree[k].r - tree[k].l + 1)*y;
    		tree[k].f += y;
    		return;
    	}
    	if (tree[k].f) down(k);
    	int m = (tree[k].l + tree[k].r) / 2;
    	if (a <= m) change_interval(k * 2);
    	if (b>m) change_interval(k * 2 + 1);
    	tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
    }
    int main()
    {
    	printf("请输入线段树的大小n
    ");
    	scanf("%d", &n);//n个节点 
    	printf("请输入n个数,分别是1到n的初始值
    ");
    	build(1, 1, n);//从1开始做下标 ,建立1到n的线段树
    	printf("建树完成
    请输入操作线段树的次数m
    ");
    	scanf("%d", &m);//m种操作 
    	printf("请输出相应指令
    1 单点查询
    2 单点修改
    3区间查询
    4区间修改
    ");
    	for (int i = 1; i <= m; i++)
    	{
    		scanf("%d", &p);
    		ans = 0;
    		if (p == 1)
    		{
    			printf("单点查询,输出第x个数 
    请输入x
    ");
    			scanf("%d", &x);
    			ask_point(1);//单点查询,输出第x个数 
    			printf("第%d个数是%d
    ", x, ans);
    		}
    		else if (p == 2)
    		{
    			printf("对第x个数加上y,请输入X和Y
    ");
    			scanf("%d%d", &x, &y);
    			change_point(1);//单点修改
    			printf("修改完成
    ");
    		}
    		else if (p == 3)
    		{
    			printf("查询[a,b]区间的和,请输入a和b
    ");
    			scanf("%d%d", &a, &b);//区间查询 
    			ask_interval(1);
    			printf(" %d 到 %d 的和为 %d
    ", a, b, ans);
    		}
    		else
    		{
    			printf("对第[a,b]中所有数都加上y,请输入a,b和y
    ");
    			scanf("%d%d%d", &a, &b, &y);//区间修改 
    			change_interval(1);
    			printf("修改完成
    ");
    		}
    	}
    }
    

    共计27个算法/数据结构   加  三块头文件函数

    制作时间:2019.4

  • 相关阅读:
    Ext JS学习第三天 我们所熟悉的javascript(二)
    Ext JS学习第二天 我们所熟悉的javascript(一)
    Ext JS学习第十七天 事件机制event(二)
    Ext JS学习第十六天 事件机制event(一)
    Ext JS学习第十五天 Ext基础之 Ext.DomQuery
    Ext JS学习第十四天 Ext基础之 Ext.DomHelper
    Ext JS学习第十三天 Ext基础之 Ext.Element
    Ext JS学习第十天 Ext基础之 扩展原生的javascript对象(二)
    针对错误 “服务器提交了协议冲突. Section=ResponseHeader Detail=CR 后面必须是 LF” 的原因分析
    C# 使用HttpWebRequest通过PHP接口 上传文件
  • 原文地址:https://www.cnblogs.com/yyzwz/p/13393276.html
Copyright © 2011-2022 走看看