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

原文地址:https://www.cnblogs.com/yyzwz/p/13393276.html