2020年2月更新:算法模板V2.1版
下载地址
前言
本模板是我在备战省赛的时光中,把复习过的和新学的算法中比较常用的代码、思路,整合成了模板,供以后的ACM竞赛直接使用,因为时间匆忙、水平有限,若有不足之处,欢迎大佬提出。本版本为V1.0版,会不定期更新。
以下所有代码都是我个人手敲,并且通过HDU、POJ等知名OJ上做题实践验证过的结果,是我两个月的成果。
水平有限,代码比较累赘,以后有机会进一步减少少考的算法,增加比较常用的算法合集。
希望能够抛砖引玉,如有意见,欢迎留言,或E-mail [email protected]
目录
前言..................................................................................................................................................................... 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\n", 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\n", *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的长度 到'\0'就算结束
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
函数库—cmath
int a = 1, b = -2, c = 3, d = 4;
double e = 1.1, f = 8.36, g = 2.2, h =3.4;
/*———————— 部分————————*/
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为直角边的三角形斜边长
/*———————— 部分————————*/
快速幂
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=m)//若达到结果
{
if(e-s+1
二分
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\n",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
#include
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
#include
#include
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
#include
#include
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] 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
#include
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\n", map[len1 % 2][len2]);
}
return 0;
}
2.o(n+m)
#include
#include
#include
#include
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\n", map[len1 + 1][len2 + 1]);
}
return 0;
}
大数a+b
#include
#include
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; ilen2 ? 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
#include
#include
#include
#include
using namespace std;
vectora;
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的最大的阶乘数
queueq;
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
#include
#include
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 >v[102];
//用vector + pair 模拟二维数组 即三个变量的对应关系
if (n == 0 && m == 0) break;
while (m--)
{
pairp;//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 >::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
#include
#include
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
#include
#include
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]> 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
#include
#include
#include
#include
#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.x0)
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= 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\n", 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)
{
queueq1;//1.先创建队列
q1.push(x);//2.放入初始条件
while (!q1.empty())//3.开始循环广搜
{
int t = q1.front();
q1.pop();//4.顶部删掉
for (vector::iterator it = v[t].begin(); it != v[t].end(); it++)
{//5.把顶部能牵连到的元素压入队列
if (a[*it] == false)
q1.push(*it);
}
}//6.全部循环结束后,如果需要返回值就返回
}
母函数或多重背包
#include
#include
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\n", 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= v[i]; j--)
{
dp[j] = max(dp[j], dp[j - v[i]] + v[i]);
}
}
}
状压DP
#include
#include
#include
#include
#include
using namespace std;
题意:有n门课,每门课有截止时间和完成所需的时间,如果超过规定时间完成,每超过一天就会扣1分,问怎样安排做作业的顺序才能使得所扣的分最小
思路:因为最多只有15门课程,可以使用二进制来表示所有完成的状况
例如5,二进制位101,代表第一门和第三门完成了,第二门没有完成,那么我们可以枚举1~1<> t;
while (t--)
{
memset(dp, 0, sizeof(dp));
cin >> n;
for (i = 0; i> a[i].name >> a[i].dead >> a[i].cost;//每门课的名字、最晚完成时间、花费时间
end = 1 << n;//n-1指的是每门课都完成的情况
for (s = 1; s= 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 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
#include
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\n");
scanf("%d", &n);//n个节点
printf("请输入n个数,分别是1到n的初始值\n");
build(1, 1, n);//从1开始做下标 ,建立1到n的线段树
printf("建树完成\n请输入操作线段树的次数m\n");
scanf("%d", &m);//m种操作
printf("请输出相应指令\n1 单点查询\n2 单点修改\n3区间查询\n4区间修改\n");
for (int i = 1; i <= m; i++)
{
scanf("%d", &p);
ans = 0;
if (p == 1)
{
printf("单点查询,输出第x个数 \n请输入x\n");
scanf("%d", &x);
ask_point(1);//单点查询,输出第x个数
printf("第%d个数是%d\n", x, ans);
}
else if (p == 2)
{
printf("对第x个数加上y,请输入X和Y\n");
scanf("%d%d", &x, &y);
change_point(1);//单点修改
printf("修改完成\n");
}
else if (p == 3)
{
printf("查询[a,b]区间的和,请输入a和b\n");
scanf("%d%d", &a, &b);//区间查询
ask_interval(1);
printf(" %d 到 %d 的和为 %d\n", a, b, ans);
}
else
{
printf("对第[a,b]中所有数都加上y,请输入a,b和y\n");
scanf("%d%d%d", &a, &b, &y);//区间修改
change_interval(1);
printf("修改完成\n");
}
}
}
共计27个算法/数据结构 加 三块头文件函数
制作时间:2019.4