AcWing算法基础课
目录
- 算法基础课
- 第一章 基础算法
- 785.快速排序
- 786.第k个数
- 787.归并排序
- 788.逆序对的数量*
- 789.数的范围
- 790.数的三次方根
- 791.高精度加法
- 792.高精度减法
- 793.高精度乘法
- 794.高精度除法*
- 759.前缀和
- 796.子矩阵的和
- 797.差分
- 798.差分矩阵*
- 799.最长连续不重复子序列
- 800.数组元素的目标和
- 2816.判断子序列
- 801.二进制中1的个数
- 802.区间和**
- 803.区间和并
- 第二章 数据结构
- 826.单链表
- 872.双链表*
- 828.模拟栈
- 3302.表达式求值**
- 829.模拟队列
- 830.单调栈
- 154.滑动窗口**
- 831.KMP字符串**
- 835.Trie字符串统计*
- 143.最大异或对*
- 836.合并集合
- 837.连通块中点的数量
- 240.食物链**
- 838.堆排序
- 839.模拟堆**
- 840.模拟散列表
- 841.字符串哈希**
- 第三章 搜索与图论
- 842.排列数字
- 844.走迷宫
- 845.八数码*
- 846.树的重心*
- 847.图中点的层次
- 848.有向图的拓扑序列
- 849.Dijkstra求最短路I
- 850.Dijkstra求最短路II
- 853.有边数限制的最短路*
- 851.spfa求最短路
- 852.spfa判断负环*
- 854.Floyd求最短路
- 858.Prim求最小生成树
- 859.Kruskal算法求最小生成树
- 860.染色法判定二分图
- 861.二分图的最大匹配*
- 第四章 数学知识
- 866.试除法判断质数
- 867.分解质因数
- 870.筛质数*
- 869.试除法求约数
- 870.约数个数
- 871.约数之和
- 872.最大公约数
- 873.欧拉函数
- 874.筛法求欧拉函数**
- 875.快速幂
- 876.快速幂求逆元*
- 877.扩展欧几里得算法*
- 878.线性同余方程**
- 204.表达整数的奇怪方式***
- 883.高斯消元解线性方程组**
- 884.高斯消元解异或线性方程组*
- 885.求组合数I
- 886.求组合数II
- 887.求组合数III
- 888.求组合数IV*
- 889.满足条件的01序列
- 890.能被整除的数
- 891.Nim游戏
- 892.台阶-Nim游戏
- 893.集合-Nim游戏*
- 894.拆分-Nim游戏**
- 第五章 动态规划
- 2.01背包
- 3.完全背包问题
- 4.多重背包问题I
- 5.多重背包问题II*
- 9.分组背包问题
- 898.数字三角形
- 895.最长上升子序列
- 896.最长上升子序列II
- 897.最长公共子序列*
- 902.最短编辑距离*
- 899.编辑距离*
- 282.石子合并
- 900.整数划分
- 338.计数问题***
- 291.蒙德里安的梦想**
- 91.最短Hamilton路径*
- 285.没有上司的舞会
- 901.滑雪
- 第六章 贪心
- 905.区间选点
- 908.最大不相交区间数量
- 906.区间分组*
- 907.区间覆盖*
- 148.合并果子
- 913.排队打水
- 104.货仓选址
- 125.耍杂技的牛*
- 第一章 基础算法
算法基础课
第一章 基础算法
785.快速排序
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int num[N];
void quickSort(int l, int r)
{
if(l >= r)
{
return;
}
int x = num[l+r>>1];
int i = l-1, j = r+1;
while(i < j)
{
do i++; while(num[i] < x); //不加等号,防止全都一样穿了整个数组
do j--; while(num[j] > x);
if(i < j) //两个指针错位的
{
swap(num[i], num[j]);
}
}
quickSort(l, j); //两个指针错位的
quickSort(j+1, r);
}
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; ++ i)
{
cin >> num[i];
}
quickSort(1, n);
for(int i = 1; i <= n; ++ i)
{
cout << num[i] << " ";
}
return 0;
}
786.第k个数
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n, k;
int num[N];
//函数作用,排序l-r并且找到l-r这个区间从小到大的第t个数
int quickSort(int l, int r, int k)
{
if(l == r)
{
return num[l];
}
int x = num[l+r>>1];
int i = l-1, j = r+1;
while(i < j)
{
do i ++; while(num[i] < x); //不取等号防穿
do j --; while(num[j] > x);
if(i < j) //i,j会错位
{
swap(num[i], num[j]);
}
}
int lcnt = j-l+1;
if(k <= lcnt)
{
return quickSort(l, j, k); //i和j错位了
}
else
{
return quickSort(j+1, r, k-lcnt);
}
}
int main()
{
cin >> n >> k;
for(int i = 1; i <= n; ++ i)
{
cin >> num[i];
}
cout << quickSort(1, n, k) << endl;
return 0;
}
787.归并排序
#include
#include
using namespace std;
const int N = 1e5+5;
int n;
int num[N];
int tp[N];
//作用:排序l-r区间
void mergeSort(int l, int r)
{
if(l >= r)
{
return ;
}
int mid = l+r>>1;
mergeSort(l, mid);
mergeSort(mid+1, r);
int i = l, j = mid+1, t = l;
while(i <= mid && j <= r)
{
tp[t++] = num[i]<=num[j]?num[i++]:num[j++];
}
while(i <= mid)
{
tp[t++] = num[i++];
}
while(j <= r)
{
tp[t++] = num[j++];
}
for(int i = l; i <= r; ++ i)
{
num[i] = tp[i];
}
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
cin >> num[i];
}
mergeSort(1, n);
for(int i = 1; i <= n; ++ i)
{
cout << num[i] << " ";
}
return 0;
}
788.逆序对的数量*
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 1e5+5;
int n;
int num[N];
int tp[N];
//对l-r区间进行排序,并且返回这个区间的逆序对数量
LL mergeSort(int l ,int r)
{
if(l >= r)
{
return 0;
}
int mid = l+r>>1;
LL lcnt = mergeSort(l, mid);
LL rcnt = mergeSort(mid+1, r);
LL res = lcnt+rcnt;
int i = l, j = mid+1, t = l;
//以右半边为基准不动点,则res += S[mid+1]+S[mid+2]+...+S[r]
while(i <= mid && j <= r)
{
if(num[i] <= num[j]) //不产生逆序对
{
tp[t++] = num[i++];
}
else //产生逆序对
{
tp[t++] = num[j++];
res += (mid-i+1);
}
}
//逆序对统计以右半边区间为基准
while(i <= mid)
{
tp[t++] = num[i++];
}
//不产生逆序对
while(j <= r)
{
tp[t++] = num[j++];
}
for(int i = l; i <= r; ++ i)
{
num[i] = tp[i];
}
return res;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
cin >> num[i];
}
cout << mergeSort(1, n) << endl;
return 0;
}
789.数的范围
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n, q;
int num[N];
int main()
{
cin >> n >> q;
for(int i = 1; i <= n; ++ i)
{
cin >> num[i];
}
while(q --)
{
int k;
cin >> k;
int resL = -1, resR = -1;
//求起始位置,即大于等于k的最小值
int l = 1, r = n;
while(l < r)
{
int mid = l+r>>1;
if(num[mid] >= k)
{
r = mid;
}
else l = mid+1;
}
if(num[l] == k)
{
resL = l-1; //位置从0开始计数
}
//求终止位置,即小于等于k的最大值
l = 1, r = n;
while(l < r)
{
int mid = l+r+1>>1;
if(num[mid] <= k)
{
l = mid;
}
else r = mid-1;
}
if(num[l] == k)
{
resR = l-1; //位置从0开始计数
}
cout << resL << " " << resR << endl;
}
return 0;
}
790.数的三次方根
#include
#include
#include
using namespace std;
const double eps = 1e-8;
double n;
int main()
{
cin >> n;
double l = -100, r = 100;
while(r-l > eps)
{
double mid = (r+l)/2;
if(mid*mid*mid >= n)
{
r = mid;
}
else
{
l = mid;
}
}
printf("%.6lf\n", l);
return 0;
}
791.高精度加法
#include
#include
#include
#include
using namespace std;
const int N = 1e5+5;
string sa, sb;
int a[N], b[N];
int main()
{
cin >> sa >> sb;
int la = sa.size(), lb = sb.size();
for(int i = la-1, j = 0; i >= 0; -- i, ++ j)
{
a[j] = sa[i]-'0';
}
for(int i = lb-1, j = 0; i >= 0; -- i, ++ j)
{
b[j] = sb[i]-'0';
}
int r = 0;
int len = max(la, lb);
int bit = 0;
//结果存到b数组
while(bit < len)
{
b[bit] += a[bit] + r;
r = b[bit]/10;
b[bit] %= 10;
bit ++;
}
if(r > 0)
{
b[bit] = r;
len ++;
}
for(int i = len-1; i >= 0; -- i)
{
cout << b[i];
}
return 0;
}
792.高精度减法
#include
#include
#include
#include
using namespace std;
const int N = 1e5+5;
string sa, sb;
int a[N], b[N];
int main()
{
cin >> sa >> sb;
int la = sa.size(), lb = sb.size();
if(la < lb || (la == lb && sa < sb))
{
cout << "-";
swap(sa, sb);
swap(la, lb);
}
for(int i = la-1, j = 0; i >= 0; -- i, ++ j)
{
a[j] = sa[i]-'0';
}
for(int i = lb-1, j = 0; i >= 0; -- i, ++ j)
{
b[j] = sb[i]-'0';
}
//计算结果存到a数组
int r = 0; //借位
for(int i = 0; i < la; ++ i)
{
a[i] = (a[i]-r) - b[i];
if(a[i] < 0)
{
a[i] += 10;
r = 1;
}
else
{
r = 0;
}
}
int idxStart = la-1;
while(a[idxStart] == 0 && idxStart >= 0)
{
idxStart --;
}
if(idxStart < 0)
{
cout << 0;
}
else
{
for(int i = idxStart; i >= 0; -- i)
{
cout << a[i];
}
}
return 0;
}
793.高精度乘法
#include
#include
#include
#include
using namespace std;
const int N = 1e5+10; //注意比平时还要多加五位
string sa;
int b;
int a[N];
int main()
{
cin >> sa >> b;
if(b == 0)
{
cout << 0 << endl;
return 0;
}
int la = sa.size();
for(int i = la-1, j = 0; i >= 0; -- i, ++ j)
{
a[j] = sa[i]-'0';
}
//结果存到a数组中
int len = la;
int r = 0; //进位
for(int i = 0; i < la; ++ i)
{
a[i] = a[i]*b+r;
r = a[i]/10;
a[i] %= 10;
}
while(r > 0)
{
a[len] = r%10;
r = r/10;
len ++;
}
for(int i = len-1; i >= 0; -- i)
{
cout << a[i];
}
return 0;
}
794.高精度除法*
#include
#include
#include
#include
#include
using namespace std;
const int N = 1e5+5;
string sa;
int b;
int main()
{
cin >> sa >> b;
int la = sa.size();
int r = 0;
vector ans;
for(int i = 0; i < la; ++ i)
{
int x = sa[i]-'0';
r = r*10+x;
int resBit = r/b;
r = r%b;
if(ans.size() == 0 && resBit == 0)
{
continue;
}
ans.push_back(resBit);
}
if(ans.size() == 0)
{
cout << 0 << endl;
}
else
{
for(int i = 0; i < ans.size(); ++ i)
{
cout << ans[i];
}
cout << endl;
}
cout << r << endl;
return 0;
}
759.前缀和
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n, m;
int sum[N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i)
{
cin >> sum[i];
sum[i] += sum[i-1];
}
while(m --)
{
int l, r;
cin >> l >> r;
cout << sum[r] - sum[l-1] << endl;
}
return 0;
}
796.子矩阵的和
#include
#include
#include
using namespace std;
const int N = 1005;
int n, m, q;
int sum[N][N];
int main()
{
cin >> n >> m >> q;
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= m; ++ j)
{
int x;
cin >> x;
sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + x;
}
}
while(q --)
{
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
cout << sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1] << endl;
}
return 0;
}
797.差分
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n, m;
int d[N];
void add(int l, int r, int x)
{
d[l] += x;
d[r+1] -= x;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i)
{
int x;
cin >> x;
add(i, i, x);
}
while(m --)
{
int l, r, c;
cin >> l >> r >> c;
add(l, r, c);
}
for(int i = 1; i <= n; ++ i)
{
d[i] += d[i-1];
cout << d[i] << " ";
}
return 0;
}
798.差分矩阵*
#include
#include
#include
using namespace std;
const int N = 1005;
int n, m, q;
int d[N][N];
void add(int x1, int y1, int x2, int y2, int c)
{
d[x1][y1] += c;
d[x1][y2+1] -= c;
d[x2+1][y1] -= c;
d[x2+1][y2+1] += c;
}
int main()
{
cin >> n >> m >> q;
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= m; ++ j)
{
int x;
cin >> x;
add(i, j, i, j, x);
}
}
while(q --)
{
int x1, y1, x2, y2, c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
add(x1, y1, x2, y2, c);
}
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= m; ++ j)
{
d[i][j] = d[i][j] + d[i-1][j] + d[i][j-1] - d[i-1][j-1];
cout << d[i][j] << " ";
}
cout << endl;
}
return 0;
}
799.最长连续不重复子序列
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n;
int num[N];
int cnt[N];
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
cin >> num[i];
}
int ans = 0;
for(int i = 1, j = 0; i <= n; ++ i) //j是开区间
{
cnt[num[i]] ++;
while(j < i && cnt[num[i]] > 1)
{
j ++;
cnt[num[j]] --;
}
ans = max(ans, i-j);
}
cout << ans << endl;
return 0;
}
800.数组元素的目标和
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n, m, x;
int num1[N], num2[N];
int main()
{
cin >> n >> m >> x;
for(int i = 1; i <= n; ++ i)
{
cin >> num1[i];
}
for(int i = 1; i <= m; ++ i)
{
cin >> num2[i];
}
for(int i = 1, j = m; i <= n && j >= 0; )
{
if(num1[i] + num2[j] > x)
{
j --;
}
else if(num1[i] + num2[j] < x)
{
i ++;
}
else
{
cout << i-1 << " " << j-1 << endl;
break;
}
}
return 0;
}
2816.判断子序列
#include
#include
using namespace std;
const int N = 1e5+5;
int n, m;
int num1[N], num2[N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i)
{
cin >> num1[i];
}
for(int i = 1; i <= m; ++ i)
{
cin >> num2[i];
}
bool ans = false;
for(int i = 1, j = 1; i <= m && j <= n; ++ i) //注意题意
{
if(num2[i] == num1[j])
{
j ++;
}
if(j > n)
{
ans = true;
}
}
if(ans)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
return 0;
}
801.二进制中1的个数
#include
#include
#include
using namespace std;
int n;
int lowbit(int x)
{
return x&(-x);
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int x;
cin >> x;
int cnt = 0;
while(x)
{
x -= lowbit(x);
cnt ++;
}
cout << cnt << " ";
}
return 0;
}
802.区间和**
#include
#include
#include
#include
#include
803.区间和并
#include
#include
#include
#define x first
#define y second
using namespace std;
typedef pair PII;
int n;
vector area;
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int l, r;
cin >> l >> r;
area.push_back({l, r});
}
sort(area.begin(), area.end());
int L = area[0].x, R = area[0].y;
int ans = 1;
for(int i = 1; i < area.size(); ++ i)
{
if(area[i].x > R)
{
ans ++;
L = area[i].x;
R = area[i].y;
}
else
{
R = max(R, area[i].y);
}
}
cout << ans << endl;
}
第二章 数据结构
826.单链表
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int value[N];
int ne[N], idx=1; //从1开始计算节点, 0是表头结点
void addHead(int x)
{
value[idx] = x;
ne[idx] = ne[0];
ne[0] = idx ++;
}
//删除第k个插入的数后面的数
void del(int k)
{
ne[k] = ne[ne[k]];
}
void add(int k, int x)
{
value[idx] = x;
ne[idx] = ne[k];
ne[k] = idx ++;
}
int main()
{
int M;
cin >> M;
while(M --)
{
char op[2];
scanf("%s", op);
if(op[0] == 'H')
{
int x;
cin >> x;
addHead(x);
}
else if(op[0] == 'D')
{
int k;
cin >> k;
del(k);
}
else
{
int k, x;
cin >> k >> x;
add(k, x);
}
}
for(int i = 0; ne[i] != 0; i = ne[i])
{
cout << value[ne[i]] << " ";
}
return 0;
}
872.双链表*
#include
#include
#include
#include
using namespace std;
const int N = 1e5+5;
//idx==1是表头结点,idx==2是表尾节点, L,R==0表示无节点
int L[N], R[N], value[N], idx=3;
void init()
{
R[1] = 2;
L[2] = 1;
idx = 3;
}
// 最左侧插入一个数
void addHead(int x)
{
value[idx] = x;
L[idx] = 1; //指向表头结点
R[idx] = R[1];
L[R[1]] = idx;
R[1] = idx;
idx ++;
}
// 最右侧插入一个数
void addTail(int x)
{
value[idx] = x;
R[idx] = 2;
L[idx] = L[2];
R[L[2]] = idx;
L[2] = idx;
idx ++;
}
// 删除第k个插入的数,下标从3开始,所以是k-2
void del(int k)
{
int t = k+2;
R[L[t]] = R[t];
L[R[t]] = L[t];
}
// 在第k个插入的数左边插入一个数
void addL(int k, int x)
{
int t = k+2;
value[idx] = x;
R[idx] = t;
L[idx] = L[t];
R[L[t]] = idx;
L[t] = idx;
idx ++;
}
// 在第k个插入的数右边插入一个数
void addR(int k, int x)
{
int t = k+2;
value[idx] = x;
L[idx] = t;
R[idx] = R[t];
L[R[t]] = idx;
R[t] = idx;
idx ++;
}
int main()
{
init();
int m;
cin >> m;
while(m --)
{
string op;
cin >> op;
if(op == "L")
{
int x;
cin >> x;
addHead(x);
}
else if(op == "R")
{
int x;
cin >> x;
addTail(x);
}
else if(op == "D")
{
int k;
cin >> k;
del(k);
}
else if(op == "IL")
{
int k, x;
cin >> k >> x;
addL(k, x);
}
else if(op == "IR")
{
int k, x;
cin >> k >> x;
addR(k, x);
}
}
for(int i = R[1]; i != 2; i = R[i])
{
cout << value[i] << " ";
}
return 0;
}
828.模拟栈
#include
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int S[N], top = -1;
void push(int x)
{
S[++top] = x;
}
void pop()
{
if(top >= 0)
{
top --;
}
}
bool empty()
{
if(top < 0)
{
return true;
}
return false;
}
int query()
{
if(top >= 0)
{
return S[top];
}
}
int main()
{
int m;
cin >> m;
while(m --)
{
string op;
cin >> op;
if(op == "push")
{
int x;
cin >> x;
push(x);
}
else if(op == "pop")
{
pop();
}
else if(op == "empty")
{
if(empty())
{
cout << "YES" << endl;
}
else
{
cout << "NO" << endl;
}
}
else
{
cout << query() << endl;
}
}
return 0;
}
3302.表达式求值**
#include
#include
#include
#include
#include
829.模拟队列
#include
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int Q[N], hh=0, tt = -1;
void push(int x)
{
Q[++tt] = x;
}
void pop()
{
if(hh <= tt)
{
hh ++;
}
}
bool empty()
{
return hh > tt;
}
int query()
{
if(hh <= tt)
{
return Q[hh];
}
}
int main()
{
int m;
cin >> m;
while(m --)
{
string op;
cin >> op;
if(op == "push")
{
int x;
cin >> x;
push(x);
}
else if(op == "pop")
{
pop();
}
else if(op == "empty")
{
if(empty())
{
cout << "YES" << endl;
}
else
{
cout << "NO" << endl;
}
}
else
{
cout << query() << endl;
}
}
return 0;
}
830.单调栈
#include
#include
#include
#include
using namespace std;
int n;
stack S;
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int x;
cin >> x;
while(S.size() && S.top() >= x)
{
S.pop();
}
if(S.size())
{
cout << S.top() << " ";
}
else
{
cout << -1 << " ";
}
S.push(x);
}
return 0;
}
154.滑动窗口**
#include
#include
#include
#include
#define x first
#define y second
using namespace std;
const int N = 1e6+5;
int n, k;
int num[N];
//求窗口的最小值,递增
deque> minQ;
//求窗口的最大值,递减
deque> maxQ;
int main()
{
cin >> n >> k;
for(int i = 1; i <= n; ++ i)
{
cin >> num[i];
}
//求min
for(int i = 1; i <= n; ++ i)
{
while(minQ.size() && minQ.back().x >= num[i])
{
minQ.pop_back();
}
minQ.push_back({num[i], i});
if(minQ.back().y - minQ.front().y + 1 > k)
{
minQ.pop_front();
}
if(i >= k)
{
cout << minQ.front().x << " ";
}
}
cout << endl;
//求max
for(int i = 1; i <= n; ++ i)
{
while(maxQ.size() > 0 && maxQ.back().x <= num[i])
{
maxQ.pop_back();
}
maxQ.push_back({num[i], i});
if(maxQ.back().y - maxQ.front().y + 1 > k)
{
maxQ.pop_front();
}
if(i >= k)
{
cout << maxQ.front().x << " ";
}
}
return 0;
}
831.KMP字符串**
#include
#include
#include
#include
using namespace std;
const int N = 1e5+5;
string S, P; //模板串P
int n, m; //n: 模板串长度
int ne[N];
int main()
{
cin >> n >> P;
cin >> m >> S;
P = " " + P; //从1开始
S = " " + S; //从1开始
//求ne[]
//ne[1] = 0;
for(int i = 2, j = 0; i <= n; ++ i) //求ne[i]
{
//不相等并且可以退
while(j && P[i] != P[j+1]) j = ne[j];
//相等:P[i] == P[j+1]
if(P[i] == P[j+1])
{
ne[i] = j+1;
j ++;
}
//else: ne[i] = 0
}
//S[i]与P[j+1]进行匹配
for(int i = 1, j = 0; i <= m; ++ i)
{
//不相等并且可以退
while(j && S[i] != P[j+1]) j = ne[j];
//相等:S[i] == P[j+1]
if(S[i] == P[j+1])
{
j ++;
if(j == n)
{
cout << i-n+1 -1 << " "; //下标从1开始变换到从0开始
j = ne[j];
}
}
}
return 0;
}
835.Trie字符串统计*
#include
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n;
int son[N][26], idx = 1; //idx==0是表头结点或者指向空节点, 这里N是节点
int cnt[N];
void insert(string s)
{
int p = 0;
for(int i = 0; i < s.size(); ++ i)
{
int c = s[i]-'0';
if(!son[p][c])
{
son[p][c] = idx ++;
}
p = son[p][c];
}
cnt[p] ++;
}
int query(string s)
{
int p = 0;
for(int i = 0; i < s.size(); ++ i)
{
int c = s[i]-'0';
if(!son[p][c])
{
return 0;
}
p = son[p][c];
}
return cnt[p];
}
int main()
{
cin >> n;
while(n --)
{
string op, x;
cin >> op >> x;
if(op == "I")
{
insert(x);
}
else
{
cout << query(x) << endl;
}
}
return 0;
}
143.最大异或对*
#include
#include
#include
using namespace std;
const int N = 1e5+5, M = N*32;
int n;
int son[M][2], idx = 1; //N是节点数, idx是待创建的节点索引, idx==0是根节点不需要创建
int cnt[M];
int num[N];
//求异或需要从高位往低位插入
//每个数有32位
void insert(int x)
{
int p = 0;
for(int i = 31; i >= 0; -- i)
{
int bit = (x>>i)&1;
if(!son[p][bit])
{
son[p][bit] = idx ++;
}
p = son[p][bit];
}
//需要尾标记,直接记录值
//注意这道题每条记录长度一样,所以可以记录0,否则值域需要变换
cnt[p] = x;
}
//寻找与x异或值最大的元素
int find(int x)
{
int p = 0;
for(int i = 31; i >= 0; -- i)
{
int bit = (x>>i)&1;
if(son[p][!bit])
{
p = son[p][!bit];
}
else
{
p = son[p][bit];
}
}
return cnt[p];
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
cin >> num[i];
insert(num[i]);
}
int ans = 0;
for(int i = 1; i <= n; ++ i)
{
ans = max(ans, find(num[i])^num[i]);
}
cout << ans << endl;
return 0;
}
836.合并集合
#include
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n, m;
int p[N]; //父节点
int init(int n)
{
for(int i = 1; i <= n; ++ i)
{
p[i] = i;
}
}
//寻找x的祖先节点
int find(int x)
{
if(p[x] != x)
{
p[x] = find(p[x]);
}
return p[x];
}
int main()
{
cin >> n >> m;
init(n);
while(m --)
{
string op;
int a, b;
cin >> op >> a >> b;
int pa = find(a), pb = find(b);
if(op == "M")
{
if(pa != pb)
{
p[pa] = pb; //pa认pb为父
}
}
else if(op == "Q")
{
if(pa == pb)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
}
}
return 0;
}
837.连通块中点的数量
#include
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n, m;
//cnt[i]: 当i为祖先节点时,当前集合的元素个数
int p[N], cnt[N];
void init(int n)
{
for(int i = 1; i <= n; ++ i)
{
p[i] = i;
cnt[i] = 1;
}
}
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
cin >> n >> m;
init(n);
while(m --)
{
string op;
cin >> op;
if(op == "C")
{
int a, b;
cin >> a >> b;
int pa = find(a), pb = find(b);
if(pa != pb)
{
p[pa] = pb; //认pb为父
cnt[pb] += cnt[pa];
}
}
else if(op == "Q1")
{
int a, b;
cin >> a >> b;
int pa = find(a), pb = find(b);
if(pa == pb)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
}
else if(op == "Q2")
{
int a;
cin >> a;
int pa = find(a);
cout << cnt[pa] << endl;
}
}
return 0;
}
240.食物链**
#include
#include
#include
using namespace std;
const int N = 5e4+5;
int n, m;
//规定,在mod3内,0->1->2->0
//d[i]表示点i到父节点的距离,路径压缩之后表示到根节点的距离
int d[N], p[N];
void init(int n)
{
for(int i = 1; i <= n; ++ i)
{
p[i] = i;
}
}
//寻找祖先节点,压缩路径,更新d[x]为到祖先的距离
int find(int x)
{
if(p[x] != x)
{
int t = find(p[x]);
d[x] = d[x] + d[p[x]];
d[x] %= 3; //优化
p[x] = t;
}
return p[x];
}
int main()
{
cin >> n >> m;
init(n);
int ans = 0;
while(m --)
{
int k, x, y;
cin >> k >> x >> y;
if(x > n || y > n)
{
ans ++;
}
else if(k == 2 && x == y)
{
ans ++;
}
else
{
int pa = find(x), pb = find(y);
if(pa != pb) //不在同一个食物链,加入!
{
if(k == 1)
{
p[pa] = pb; //pa认pb作父
d[pa] = (d[y]-d[x]+3)%3;
}
else if(k == 2)
{
p[pa] = pb; //pa认pb作父
d[pa] = (d[y]-d[x]-1+3)%3;
}
}
else //在一个食物链,判断真假
{
//这里d[x]、d[y]由于前面进行了find,所以是到根节点的距离
if(k == 1 && d[x]%3 != d[y]%3)
{
ans ++;
}
else if(k == 2 && (d[x]%3+1)%3 != d[y]%3)
{
ans ++;
}
}
}
}
cout << ans << endl;
return 0;
}
838.堆排序
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n, m;
int num[N];
//小根堆
int tree[N], idx; //idx从1开始,也表示元素数量
void up(int i)
{
//有根节点并且当前节点值比根节点小
while(i/2 > 0 && tree[i] = 1)
{
swap(tree[1], tree[idx]);
idx --;
down(1);
}
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i)
{
int x;
cin >> x;
insert(x);
}
while(m --)
{
cout << tree[1] << " ";
pop();
}
return 0;
}
839.模拟堆**
#include
#include
#include
#include
840.模拟散列表
#include
#include
#include
#include
#include
using namespace std;
const int N = 1e5+5, null = 0x3f3f3f3f;
int n;
int h[N];
int find(int x)
{
int t = (x%N+N)%N;
while(h[t] != null && h[t] != x)
{
t ++;
if(t == N)
{
t = 0;
}
}
return t;
}
int main()
{
memset(h, 0x3f, sizeof(h));
cin >> n;
while(n --)
{
string op;
int x;
cin >> op >> x;
if(op == "I")
{
int pos = find(x);
h[pos] = x;
}
else if(op == "Q")
{
int pos = find(x);
if(h[pos] == x)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
}
}
}
841.字符串哈希**
#include
#include
#include
#include
using namespace std;
typedef unsigned long long ULL;
const int N = 1e5+5, P = 131; //mod=2^64, 数据溢出会自动取模
int n, m;
string s;
ULL h[N], p[N]; //h[i]:字符串前缀哈希,p[i]:P的i次方
//未进行冲突判断,我们完全看运气, p==131或13331,mod=2^64效果最好
ULL get(int l, int r)
{
return h[r] - h[l-1]*p[r-l+1]; //数据溢出会自动取模
}
int main()
{
cin >> n >> m;
cin >> s;
s = " " + s;
p[0] = 1;
for(int i = 1; i <= n; ++ i)
{
p[i] = p[i-1]*P; //数据溢出会自动取模
h[i] = h[i-1]*P+s[i]; //数据溢出会自动取模
}
while(m --)
{
int l1, r1, l2, r2;
cin >> l1 >> r1 >> l2 >> r2;
if(get(l1, r1) == get(l2, r2))
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
}
return 0;
}
第三章 搜索与图论
842.排列数字
#include
#include
using namespace std;
const int N = 10;
int n;
int ans[N];
bool st[N]; //st[i]表示数i是否被使用
//已经选择了t个数
void dfs(int t)
{
if(t >= n)
{
for(int i = 1; i <= n; ++ i)
{
cout << ans[i] << " ";
}
cout << endl;
}
else
{
for(int i = 1; i <= n; ++ i)
{
if(!st[i])
{
st[i] = true;
ans[t+1] = i;
dfs(t+1);
ans[t+1] = 0;
st[i] = false;
}
}
}
}
int main()
{
cin >> n;
dfs(0);
return 0;
}
#include
#include
#include
using namespace std;
const int N = 12;
int n;
bool st[N]; //st[i]表示第i列有没有放
//dg[x]斜边x = i+j rdg[x]反斜边x=i+(n+1-j), 从2开始
bool dg[N*2], rdg[N*2];
int ans[N], cnt;
//当前在第u行放棋子, u从1开始, 已经放了t个棋子
void dfs(int u, int t)
{
if(u > n)
{
if(t == n)
{
if(cnt > 0)
{
cout << endl;
}
cnt++;
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= n; ++ j)
{
if(ans[i] == j)
{
cout << "Q";
}
else
{
cout << ".";
}
}
cout << endl;
}
}
return;
}
for(int j = 1; j <= n; ++ j)
{
if(!st[j] && !dg[u+j] && !rdg[u+(n+1-j)])
{
st[j] = true;
dg[u+j] = true;
rdg[u+(n+1-j)] = true;
ans[u] = j;
dfs(u+1, t+1);
ans[u] = 0;
st[j] = false;
dg[u+j] = false;
rdg[u+(n+1-j)] = false;
}
}
}
int main()
{
cin >> n;
dfs(1, 0);
return 0;
}
844.走迷宫
#include
#include
#include
#include
#include
#define x first
#define y second
using namespace std;
const int N = 105;
typedef pair PII;
int n, m;
int g[N][N]; //g[i][j]==1表示不可通过
int d[N][N]; //d[i][j]表示移动到(i, j)的需要移动的次数, -1表示未到达
void bfs(int x0, int y0)
{
memset(d, -1, sizeof d);
int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};
queue Q;
Q.push({x0, y0});
d[x0][y0] = 0; //访问过了
while(Q.size())
{
PII t = Q.front();
Q.pop();
for(int i = 0; i < 4; ++ i)
{
int a = t.x + dx[i], b = t.y + dy[i];
if(a < 1 || a > n || b < 1 || b > m) continue;
if(g[a][b] == 1 || d[a][b] != -1) //不可达或者已到达
{
continue;
}
d[a][b] = d[t.x][t.y] + 1;
Q.push({a, b});
}
}
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= m; ++ j)
{
cin >> g[i][j];
}
}
bfs(1, 1);
cout << d[n][m] << endl;
return 0;
}
845.八数码*
#include
#include
#include
#include
#include
#include
using namespace std;
//存状态对应的步数. 这题用map会超时,要用unordered_map
unordered_map d;
string endState = "12345678x";
int bfs(string s0)
{
queue Q;
Q.push(s0);
d[s0] = 0;
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
int step = 0;
while(Q.size())
{
string t = Q.front();
Q.pop();
if(t == endState)
{
return d[t];
}
int idx = t.find('x');
int x = idx/3, y = idx%3;
for(int i = 0; i < 4; ++ i)
{
int a = x+dx[i], b = y+dy[i];
if(a < 0 || a >= 3 || b < 0 || b >= 3)
{
continue;
}
int newIdx = a*3+b;
string tp = t;
swap(tp[idx], tp[newIdx]);
if(d.count(tp) > 0) //计算过了
{
continue;
}
d[tp] = d[t] + 1;
Q.push(tp);
}
}
return -1;
}
int main()
{
string start = "";
for(int i = 1; i <= 9; ++ i)
{
string tp;
cin >> tp;
start += tp;
}
cout << bfs(start) << endl;
return 0;
}
846.树的重心*
#include
#include
#include
#include
using namespace std;
//无向边
const int N = 1e5+5, M = N*2, INF = 0x3f3f3f3f;
int n;
int h[N], e[M], ne[M], idx;
void init()
{
memset(h, -1, sizeof h);
}
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
bool st[N]; //st[i]表示dfs过程中i节点是否被访问过
int ans = INF; //求最小值
//返回以root为根的子树的的节点数量
//在遍历过程中,求删除该点的最大连通块点数,
//并更新全局结果ans, 求出最小的ans
int dfs(int root)
{
st[root] = true;
int totalCnt = 1, maxCnt = 0;
for(int i = h[root]; ~i; i=ne[i])
{
int j = e[i];
if(st[j]) continue;
int sizej = dfs(j);
maxCnt = max(maxCnt, sizej);
totalCnt += sizej;
}
maxCnt = max(maxCnt, n-totalCnt);
ans = min(ans, maxCnt);
return totalCnt;
}
int main()
{
init();
cin >> n;
for(int i = 1; i <= n-1; ++ i)
{
int a, b;
cin >> a >> b;
add(a, b);
add(b, a);
}
dfs(1);
cout << ans << endl;
return 0;
}
847.图中点的层次
#include
#include
#include
#include
#include
using namespace std;
//有向图
const int N = 1e5+5, M = N;
int n, m;
int h[N], e[M], ne[M], idx;
void init()
{
memset(h, -1, sizeof h);
}
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
int d[N]; //从1号点到i号点的距离, ==-1表示没被访问过
int bfs(int s)
{
memset(d, -1, sizeof d);
queue Q;
d[s] = 0;
Q.push(s);
while(Q.size())
{
int t = Q.front();
Q.pop();
for(int i = h[t]; ~i; i=ne[i])
{
int j = e[i];
if(d[j] != -1) continue; //访问过
d[j] = d[t] + 1;
Q.push(j);
}
}
return d[n];
}
int main()
{
init();
cin >> n >> m;
for(int i = 1; i <= m; ++ i)
{
int a, b;
cin >> a >> b;
add(a, b);
}
cout << bfs(1) << endl;
return 0;
}
848.有向图的拓扑序列
#include
#include
#include
#include
#include
#include
using namespace std;
//有向图
const int N = 1e5+5, M = N;
int n, m;
int h[N], e[M], ne[M] ,idx;
int din[N]; //入度
void init()
{
memset(h, -1, sizeof h);
}
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
void top()
{
vector ans;
queue Q;
for(int i = 1; i <= n; ++ i)
{
if(din[i] == 0)
{
Q.push(i);
}
}
//出栈顺序即为一个top序
while(Q.size())
{
int t = Q.front();
Q.pop();
ans.push_back(t);
for(int i = h[t]; ~i; i=ne[i])
{
int j = e[i];
din[j] --;
if(din[j] == 0)
{
Q.push(j);
}
}
}
if(ans.size() < n)
{
cout << -1 << endl;
}
else
{
for(int i = 0; i < n; ++ i)
{
cout << ans[i] << " ";
}
}
}
int main()
{
init();
cin >> n >> m;
for(int i = 1; i <= m; ++ i)
{
int a, b;
cin >> a >> b;
//top可以去掉重边,top遇到自环直接失败
add(a, b);
din[b] ++;
}
top();
return 0;
}
849.Dijkstra求最短路I
#include
#include
#include
#include
using namespace std;
const int N = 505, M = 1e5+5, INF = 0x3f3f3f3f;
int n, m;
int h[N], e[M], ne[M], w[M], idx;
void init()
{
memset(h, -1, sizeof h);
}
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
int d[N]; //d[i]表示第1个节点到第i个节点的距离
bool st[N]; //属于左边点的集合
int dijkstra()
{
memset(d, 0x3f, sizeof d);
d[1] = 0;
for(int T = 1; T <= n; ++ T)
{
//寻找不在st中的最小d的点
int t = -1;
for(int i = 1; i <= n; ++ i)
{
if(!st[i] && (t == -1 || d[i] < d[t]))
{
t = i;
}
}
st[t] = true;
//遍历所有t的邻边
for(int i = h[t]; ~i; i=ne[i])
{
int j = e[i];
if(d[j] > d[t] + w[i])
{
d[j] = d[t] + w[i];
}
}
}
if(d[n] == INF)
{
return -1;
}
return d[n];
}
int main()
{
init();
cin >> n >> m;
for(int i = 1; i <= m; ++ i)
{
int x, y, z;
cin >> x >> y >> z; //dijkstra可以自己解决重边和自环
add(x, y, z);
}
cout << dijkstra() << endl;
}
850.Dijkstra求最短路II
#include
#include
#include
#include
#include
#define x first
#define y second
using namespace std;
//有向图
const int N = 1.5e5+5, M = N, INF = 0x3f3f3f3f;
typedef pair PII;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
void init()
{
memset(h, -1, sizeof h);
}
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
int d[N]; //从节点1到节点i的最短距离
bool st[N]; //st[i]表示点i是否加入左边集合
int dijkstra()
{
memset(d, 0x3f, sizeof d);
priority_queue, greater> heap;
d[1] = 0;
heap.push({d[1], 1});
while(heap.size())
{
auto t = heap.top();
heap.pop();
if(st[t.y]) continue;
st[t.y] = true;
for(int i = h[t.y]; ~i; i=ne[i])
{
int j = e[i];
if(d[j] > d[t.y] + w[i])
{
d[j] = d[t.y] + w[i];
heap.push({d[j], j});
}
}
}
if(d[n] == INF)
{
return -1;
}
return d[n];
}
int main()
{
init();
cin >> n >> m;
for(int i = 1; i <= m; ++ i)
{
int x, y, z;
cin >> x >> y >> z;
add(x, y, z); //dijkstra可以解决重边和自环
}
cout << dijkstra() << endl;
return 0;
}
853.有边数限制的最短路*
#include
#include
#include
#include
using namespace std;
const int N = 505, M = 1e4+5, INF = 0x3f3f3f3f;
int n, m, k;
struct Edge
{
int x, y, z;
}edge[M];
int d[N], backup[N];
int bf()
{
memset(d, 0x3f, sizeof d);
d[1] = 0;
for(int i = 1; i <= k; ++ i)
{
memcpy(backup, d, sizeof d);
for(int j = 1; j <= m; ++ j)
{
int a = edge[j].x, b = edge[j].y, w = edge[j].z;
d[b] = min(d[b], backup[a] + w);
}
}
return d[n];
}
int main()
{
cin >> n >> m >> k;
for(int i = 1; i <= m; ++ i)
{
cin >> edge[i].x >> edge[i].y >> edge[i].z;
}
int ans = bf();
if(ans > INF/2)
{
cout << "impossible" << endl;
}
else
{
cout << ans << endl;
}
return 0;
}
851.spfa求最短路
#include
#include
#include
#include
#include
using namespace std;
//有向边
const int N = 1e5+5, M = N, INF = 0x3f3f3f3f;
int n, m;
int h[N], e[M], ne[M], w[M], idx;
void init()
{
memset(h, -1, sizeof h);
}
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
int d[N];//表示点1到点i的最短距离
//st[i]表示点i是否在队列中,避免队列存在多个重复元素
bool st[N];
int spfa()
{
queue Q;
memset(d, 0x3f, sizeof d);
d[1] = 0;
Q.push(1);
st[1] = true;
while(Q.size())
{
int t = Q.front();
Q.pop();
st[t] = false;
for(int i = h[t]; ~i; i=ne[i])
{
int j = e[i];
if(d[j] > d[t] + w[i])
{
d[j] = d[t] + w[i];
if(!st[j])
{
Q.push(j);
st[j] = true;
}
}
}
}
return d[n];
}
int main()
{
init();
cin >> n >> m;
for(int i = 1; i <= m; ++ i)
{
int x, y, z;
cin >> x >> y >> z;
add(x, y, z);
}
int ans = spfa();
if(ans == INF)
{
cout << "impossible" << endl;
}
else
{
cout << ans << endl;
}
return 0;
}
852.spfa判断负环*
#include
#include
#include
#include
#include
using namespace std;
const int N = 2005, M = 1e4+5;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
void init()
{
memset(h, -1, sizeof h);
}
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
int d[N]; //从超级源点到点i的最短距离
int cnt[N]; //对应的最短距离经过的边数
bool st[N]; //点i在不在队列中
int spfa()
{
memset(d, 0x3f, sizeof d);
queue Q;
d[0] = 0;
cnt[0] = 0;
Q.push(0);
st[0] = true;
while(Q.size())
{
int t = Q.front();
Q.pop();
st[t] = false;
for(int i = h[t]; ~i; i=ne[i])
{
int j = e[i];
if(d[j] > d[t] + w[i])
{
d[j] = d[t] + w[i];
cnt[j] = cnt[t] + 1;
if(cnt[j] > n) //多了一个超级源点
{
return true; //存在负环
}
if(!st[j])
{
Q.push(j);
}
}
}
}
return false;
}
int main()
{
init();
cin >> n >> m;
for(int i = 1; i <= m; ++ i)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
for(int i = 1; i <= n; ++ i)
{
//建立超级源点,避免不连通
//注意多建立一个点之后,最大边数变为了n
add(0, i, 0);
}
if(spfa())
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
return 0;
}
854.Floyd求最短路
#include
#include
#include
#include
using namespace std;
//有向边
const int N = 205, M = 2e4+5, INF = 0x3f3f3f3f;
int n, m, q;
int dist[N][N]; //邻接矩阵
void floyd()
{
for(int k = 1; k <= n; ++ k)
{
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= n; ++ j)
{
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
int main()
{
cin >> n >> m >> q;
memset(dist, 0x3f, sizeof dist);
for(int i = 1; i <= n; ++ i)
{
dist[i][i] = 0; //防止自环
}
for(int i = 1; i <= m; ++ i)
{
int a, b, c;
cin >> a >> b >> c;
dist[a][b] = min(dist[a][b], c); //防止自环、重边
}
floyd();
while(q --)
{
int a, b;
cin >> a >> b;
if(dist[a][b] > INF/2) //负权边+不连通
{
cout << "impossible" << endl;
}
else
{
cout << dist[a][b] << endl;
}
}
return 0;
}
858.Prim求最小生成树
#include
#include
#include
#include
using namespace std;
//无向边
const int N = 505, M = 2e5+5, INF = 0x3f3f3f3f;
int n, m;
int dist[N][N]; //稠密图,邻接矩阵
int d[N]; //d[i]表示点i到集合的距离
bool st[N]; //在不在集合中
int prim()
{
int res = 0;
memset(d, 0x3f, sizeof d);
//每轮找一个距离集合最近的点
for(int T = 1; T <= n; ++ T)
{
int t = -1;
for(int i = 1; i <= n; ++ i)
{
if(!st[i] && (t == -1 || d[i] < d[t]))
{
t = i;
}
}
if(t != 1 && d[t] == INF)
{
return INF; //不连通
}
st[t] = true; //注意顺序,避免自身被负自环更新
if(t != 1) res += d[t]; //从第二个点开始
//更新其他集合外的点
for(int i = 1; i <= n; ++ i)
{
if(!st[i])
{
d[i] = min(d[i], dist[t][i]);
}
}
}
return res;
}
int main()
{
cin >> n >> m;
memset(dist, 0x3f, sizeof dist);
for(int i = 1; i <= m; ++ i)
{
int a, b, c;
cin >> a >> b >> c; //无向边
dist[a][b] = min(dist[a][b], c); //去掉重边
dist[b][a] = min(dist[b][a], c); //去掉重边
}
int ans = prim();
if(ans == INF)
{
cout << "impossible" << endl;
}
else
{
cout << ans << endl;
}
return 0;
}
859.Kruskal算法求最小生成树
#include
#include
#include
#include
using namespace std;
//无向图
const int N = 1e5+5, M = 4e5+5, INF = 0x3f3f3f3f;
int n, m;
int p[N]; //并查集
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
struct Edge
{
int a, b, w;
bool operator<(const Edge& e)const{
return w < e.w;
}
}edge[M];
void init(int n)
{
for(int i = 1; i <= n; ++ i)
{
p[i] = i;
}
}
int kruskal()
{
int ans = 0;
int cnt = 0;
sort(edge, edge+m);
for(int i = 0; i < m; ++ i)
{
int a = edge[i].a, b = edge[i].b, w = edge[i].w;
int fa = find(a), fb = find(b);
if(fa != fb)
{
ans += w;
cnt ++;
p[fa] = fb;
}
}
if(cnt != n-1) //不连通
{
return INF;
}
return ans;
}
int main()
{
cin >> n >> m;
init(n);
for(int i = 0; i < m; ++ i)
{
cin >> edge[i].a >> edge[i].b >> edge[i].w;
}
int ans = kruskal();
if(ans == INF)
{
cout << "impossible" << endl;
}
else
{
cout << ans << endl;
}
return 0;
}
860.染色法判定二分图
#include
#include
#include
#include
using namespace std;
const int N = 1e5+5, M = 2*N;
int n, m;
int h[N], e[M], ne[M], idx;
void init()
{
memset(h, -1, sizeof h);
}
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
int color[N]; //0表示没染色,1、2分别表示两种颜色
//从u点开始染色,染成c颜色
bool dfs(int u, int c)
{
color[u] = c;
for(int i = h[u]; ~i; i=ne[i])
{
int j = e[i];
if(!color[j])
{
if(!dfs(j, 3-c))
{
return false;
}
}
else if(color[j] == c) //这个也可以判掉自环
{
return false;
}//注:前后两个if判断情况可以去掉重边
}
return true;
}
int main()
{
cin >> n >> m;
init();
for(int i = 1; i <= m; ++ i)
{
int a, b;
cin >> a >> b;
add(a, b);
add(b, a);
}
bool ans = true;
for(int i = 1; i <= n; ++ i) //避免不连通
{
if(!color[i])
{
if(!dfs(i, 1))
{
ans = false;
break;
}
}
}
if(ans)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
return 0;
}
861.二分图的最大匹配*
#include
#include
#include
#include
using namespace std;
//有向边,从男生指向女生
const int N = 505, M = 1e5+5;
int n1, n2, m;
int h[N], e[M], ne[M], idx;
void init()
{
memset(h, -1, sizeof h);
}
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
int match[N];
bool st[N];
bool find(int x)
{
for(int i = h[x]; ~i; i=ne[i])//遍历所有喜欢的女孩子
{
int j = e[i];
if(!st[j]) //没有考虑过
{
st[j] = true;
if(match[j] == 0 || find(match[j]))
{
match[j] = x;
return true;
}
}
}
return false;
}
int main()
{
cin >> n1 >> n2 >> m;
init();
for(int i = 1; i <= m; ++ i)
{
int a, b;
cin >> a >> b;
//因为ab属于不同的集合,并且方向单向,所以两个集合序号可以相同
add(a, b);
}
int ans = 0;
for(int i = 1; i <= n1; ++ i)
{
memset(st, false, sizeof st); //所有女孩子都可以考虑
if(find(i)) //找到女朋友
{
ans ++;
}
}
cout << ans << endl;
return 0;
}
第四章 数学知识
866.试除法判断质数
#include
#include
#include
using namespace std;
int n;
bool isPrime(int x)
{
if(x < 2) return false;
for(int i = 2; i <= x/i; ++ i)
{
if(x%i==0) return false;
}
return true;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int x;
cin >> x;
if(isPrime(x))
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
}
return 0;
}
867.分解质因数
#include
#include
#include
using namespace std;
int n;
void divide(int x)
{
for(int i = 2; i <= x/i; ++ i)
{
if(x%i==0)
{
int cnt = 0;
while(x%i==0)
{
x /= i;
cnt ++;
}
cout << i << " " << cnt << endl;
}
}
if(x > 1)
{
cout << x << " " << 1 << endl;
}
cout << endl;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int x;
cin >> x;
divide(x);
}
return 0;
}
870.筛质数*
#include
#include
#include
using namespace std;
const int N = 1e6+5;
int n;
int prime[N], cnt;
bool st[N]; //是否被筛掉
void getPrime(int n)
{
for(int i = 2; i <= n; ++ i)
{
if(!st[i]) //质数
{
prime[cnt++] = i;
}
for(int j = 0; j < cnt && prime[j] <= n/i; ++ j)
{
st[i*prime[j]] = true; //pj是pj*i的最小质因子
if(i%prime[j] == 0) break; //pj是i的最小质因子
}
}
}
int main()
{
cin >> n;
getPrime(n);
cout << cnt << endl;
return 0;
}
869.试除法求约数
#include
#include
#include
#include
using namespace std;
int n;
vector getDivisors(int n)
{
vector ans;
for(int i = 1; i <= n/i; ++ i)
{
if(n%i==0)
{
ans.push_back(i);
if(i != n/i) ans.push_back(n/i);
}
}
sort(ans.begin(), ans.end());
return ans;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int a;
cin >> a;
vector ans = getDivisors(a);
for(int i = 0; i < ans.size(); ++ i)
{
cout << ans[i] << " ";
}
cout << endl;
}
return 0;
}
870.约数个数
#include
#include
#include
#include
#define x first
#define y second
using namespace std;
const int MOD = 1e9+7;
typedef long long LL;
int n;
unordered_map ha;
void divide(int n)
{
for(int i = 2; i <= n/i; ++ i)
{
while(n%i==0)
{
ha[i] ++;
n /= i;
}
}
if(n > 1) ha[n] ++;
}
int main()
{
cin >> n;
int ans = 1;
for(int i = 1; i <= n; ++ i)
{
int a;
cin >> a;
divide(a);
}
for(auto prime: ha)
{
ans = (LL)ans*(prime.y+1)%MOD;
}
cout << ans << endl;
return 0;
}
871.约数之和
#include
#include
#include
#include
#define x first
#define y second
using namespace std;
typedef long long LL;
const int MOD = 1e9+7;
int n;
unordered_map ha;
void divide(int n)
{
for(int i = 2; i <= n/i; ++ i)
{
while(n%i==0)
{
ha[i] ++;
n /= i;
}
}
if(n > 1) ha[n] ++;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int a;
cin >> a;
divide(a);
}
int ans = 1;
for(auto prime: ha)
{
int p = prime.x, a = prime.y;
int t = 1;
while(a --)
{
t = ((LL)t*p+1)%MOD;
}
ans = (LL)ans*t%MOD;
}
cout << ans << endl;
return 0;
}
872.最大公约数
#include
#include
#include
using namespace std;
int n;
int gcd(int a, int b)
{
return b? gcd(b, a%b) : a;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int a, b;
cin >> a >> b;
cout << gcd(a, b) << endl;
}
return 0;
}
873.欧拉函数
#include
#include
#include
#include
using namespace std;
const int N = 1700;
int n;
int prime[N], cnt;
void divide(int n)
{
cnt = 0;
memset(prime, 0, sizeof prime);
for(int i = 2; i <= n/i; ++ i)
{
if(n%i==0)
{
prime[cnt ++] = i;
while(n%i==0)
{
n /= i;
}
}
}
if(n > 1) prime[cnt++] = n;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int a;
cin >> a;
divide(a);
int ans = a;
for(int i = 0; i < cnt; ++ i)
{
ans = ans/prime[i]*(prime[i]-1);
}
cout << ans << endl;
}
return 0;
}
874.筛法求欧拉函数**
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 1e6+5;
int n;
int prime[N], cnt;
int euler[N];
bool st[N];
void getEulers(int n)
{
euler[1] = 1;
for(int i = 2; i <= n; ++ i)
{
if(!st[i])
{
prime[cnt++] = i;
euler[i] = i-1;
}
for(int j = 0; j < cnt && prime[j] <= n/i; ++ j)
{
st[prime[j]*i] = true; //筛去
if(i % prime[j] == 0)
{
euler[prime[j]*i] = euler[i]*prime[j];
break;
}
else
{
euler[prime[j]*i] = euler[i]*(prime[j] - 1);
}
}
}
}
int main()
{
cin >> n;
getEulers(n);
LL ans = 0;
for(int i = 1; i <= n; ++ i)
{
ans += euler[i];
}
cout << ans << endl;
return 0;
}
875.快速幂
#include
#include
#include
using namespace std;
typedef long long LL;
int n;
int qmi(int a, int b, int p)
{
int ans = 1;
while(b)
{
if(b&1) ans = ((LL)ans*a)%p;
b >>= 1;
a = ((LL)a*a)%p;
}
return ans;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int a, b, p;
cin >> a >> b >> p;
cout << qmi(a, b, p) << endl;
}
return 0;
}
876.快速幂求逆元*
#include
#include
#include
using namespace std;
typedef long long LL;
int n;
int qmi(int a, int b, int p)
{
int ans = 1;
while(b)
{
if(b&1) ans = ((LL)ans*a)%p;
b >>= 1;
a = ((LL)a*a)%p;
}
return ans;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int a, p;
cin >> a >> p;
//p必须是质数, 并且a不是p的倍数
if(a%p==0) cout << "impossible" << endl;
else cout << qmi(a, p-2, p) << endl;
}
return 0;
}
877.扩展欧几里得算法*
#include
#include
#include
using namespace std;
int n;
int exgcd(int a, int b, int &x, int &y)
{
if(b == 0)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a%b, y, x);
y = y - a/b*x;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int a, b, x, y;
cin >> a >> b;
exgcd(a, b, x, y);
cout << x << " " << y << endl;
}
}
878.线性同余方程**
#include
#include
#include
using namespace std;
typedef long long LL;
int n;
int exgcd(int a, int b, int &x, int &y)
{
if(b == 0)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a%b, y, x);
y = y - a/b*x;
return d;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int a, b, m, x, y;
cin >> a >> b >> m;
int d = exgcd(a, m, x, y);
if(b%d==0)
{
cout << (LL)b/d*x%m << endl; //b随意给,所以注意这里会爆
}
else
{
cout << "impossible" << endl;
}
}
return 0;
}
204.表达整数的奇怪方式***
#include
#include
#include
using namespace std;
typedef long long LL;
int n;
int exgcd(LL a, LL b, LL &x, LL &y)
{
if(b == 0)
{
x = 1, y = 0;
return a;
}
else
{
LL d = exgcd(b, a%b, y, x);
y -= (a/b*x);
return d;
}
}
int main()
{
cin >> n;
LL lasta, lastm;
cin >> lasta >> lastm;
bool ans = true;
for(LL i = 2; i <= n; ++ i)
{
LL a, m;
cin >> a >> m;
LL k1, k2;
LL d = exgcd(lasta, -a, k1, k2);
if((m-lastm)%d!=0)
{
ans = false;
break;
}
k1 = k1*(m-lastm)/d;
k2 = k2*(m-lastm)/d;
LL t = a/d;
k1 = (k1%t+t)%t;
lastm = k1*lasta+lastm;
lasta = abs(lasta*a/d);
}
if(ans)
{
cout << (lastm%lasta+lasta)%lasta << endl;
}
else
{
cout << -1 << endl;
}
return 0;
}
883.高斯消元解线性方程组**
#include
#include
#include
#include
using namespace std;
const int N = 105;
const double eps = 1e-4;
int n;
double a[N][N];
int gauss()
{
int c = 1, r = 1; //r是当前需要工作的行数
for(; c <= n; ++ c)
{
//找到c列绝对值最大的元素
int idx = -1;
for(int i = r; i <= n; ++ i)
{
if(idx == -1 || fabs(a[i][c]) > fabs(a[idx][c]))
{
idx = i;
}
}
if(fabs(a[idx][c]) < eps) //最大为0
{
// cout << "!!!" << endl;
continue;
}
if(r != idx) //idx行和r行交换
{
for(int j = 1; j <= n+1; ++ j)
{
swap(a[r][j], a[idx][j]);
}
}
//将r行c列元素变成1
for(int j = n+1; j >= c; -- j)
{
a[r][j] /= a[r][c];
}
for(int i = r+1; i <= n; ++ i) //i行开头变0
{
if(fabs(a[i][c]) < eps) continue;
for(int j = n+1; j >= c; -- j)
{
a[i][j] -= a[r][j]*a[i][c];
}
}
r ++;
}
if(r <= n)
{
for(int i = r; i <= n; ++ i)
{
if(fabs(a[i][n+1]) > eps) return 1; //无解
}
return 2; //无穷多组解
}
for(int i = n; i > 0; -- i)
{
for(int j = n; j >= i+1; -- j)
{
a[i][n+1] -= a[i][j]*a[j][n+1];
}
}
return 0;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= n+1; ++ j)
{
cin >> a[i][j];
}
}
int ans = gauss();
if(ans == 1)
{
cout << "No solution" << endl;
}
else if(ans == 2)
{
cout << "Infinite group solutions" << endl;
}
else
{
for(int i = 1; i <= n; ++ i)
{
printf("%.2f\n", a[i][n+1]);
}
}
return 0;
}
884.高斯消元解异或线性方程组*
#include
#include
#include
using namespace std;
const int N = 105;
int n;
int a[N][N];
int guass()
{
int c = 1, r = 1;
for(; c <= n; ++ c)
{
//找到开头最大行
int idx = -1;
for(int i = r; i <= n; ++ i)
{
if(idx == -1 || a[i][c] > a[idx][c])
{
idx = i;
}
}
if(a[idx][c] == 0)
{
continue;
}
//换到第r行
if(idx != r)
{
for(int j = c; j <= n+1; ++ j)
{
swap(a[idx][j], a[r][j]);
}
}
for(int i = r+1; i <= n; ++ i)
{
if(a[i][c] == 1) //注意
{
for(int j = c; j <= n+1; ++ j)
{
a[i][j] ^= a[r][j];
}
}
}
r ++;
}
if(r <= n)
{
for(int i = r; i <= n; ++ i)
{
if(a[i][n+1] != 0) return 1; //无解
}
return 2; //无穷多组解
}
//反向求解
for(int i = n; i > 0; -- i)
{
for(int j = i+1; j <= n; ++ j)
{
if(a[i][j] != 0)
{
a[i][n+1] ^= a[j][n+1]; //这里注意
}
}
}
return 0;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= n+1; ++ j)
{
cin >> a[i][j];
}
}
int ans = guass();
if(ans == 1)
{
cout << "No solution" << endl;
}
else if(ans == 2)
{
cout << "Multiple sets of solutions" << endl;
}
else
{
for(int i = 1; i <= n; ++ i)
{
cout << a[i][n+1] << endl;
}
}
return 0;
}
885.求组合数I
#include
#include
#include
using namespace std;
const int N = 2005, MOD = 1e9+7;
int n;
int C[N][N];
void init()
{
for(int i = 0; i < N; ++ i)
{
for(int j = 0; j <= i; ++ j)
{
if(j == 0) C[i][j] = 1;
else C[i][j] = (C[i-1][j] + C[i-1][j-1])%MOD;
}
}
}
int main()
{
cin >> n;
init();
for(int i = 1; i <= n; ++ i)
{
int a, b;
cin >> a >> b;
cout << C[a][b] << endl;
}
return 0;
}
886.求组合数II
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 1e5+5, MOD = 1e9+7;
int n;
int fact[N], infact[N]; //阶乘与阶乘的逆元
int qmi(int a, int b)
{
int ans = 1;
while(b)
{
if(b&1) ans = ((LL)ans*a)%MOD;
b >>= 1;
a = ((LL)a*a)%MOD;
}
return ans;
}
void init()
{
fact[0] = 1;
infact[0] = 1;
for(int i = 1; i < N; ++ i)
{
fact[i] = ((LL)fact[i-1]*i)%MOD;
infact[i] = qmi(fact[i], MOD-2);
}
}
int main()
{
cin >> n;
init();
while(n --)
{
int a, b;
cin >> a >> b;
cout << (LL)fact[a]*infact[b]%MOD*infact[a-b]%MOD << endl;
}
return 0;
}
887.求组合数III
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 1e5+5;
int n;
int qmi(int a, int b, int p)
{
int ans = 1;
while(b)
{
if(b&1) ans = (LL)ans*a%p;
b >>= 1;
a = (LL)a*a%p;
}
return ans;
}
int C(int a, int b, int p)
{
int res = 1;
for(int i = a-b+1; i <= a; ++ i)
{
res = (LL)res*i%p;
}
for(int i = 1; i <= b; ++ i)
{
res = (LL)res*qmi(i, p-2, p)%p;
}
return res;
}
int lucas(LL a, LL b, int p)
{
if(a < p && b < p)
{
return C(a, b, p);
}
return (LL)C(a%p, b%p, p)*lucas(a/p, b/p, p)%p;
}
int main()
{
cin >> n;
while(n --)
{
LL a, b;
int p;
cin >> a >> b >> p;
cout << lucas(a, b, p) << endl;
}
return 0;
}
888.求组合数IV*
#include
#include
#include
#include
using namespace std;
const int N = 5005;
int a, b;
int prime[N], cnt;
int sum[N];
bool st[N];
void get_primes(int n)
{
for(int i = 2; i <= n; ++ i)
{
if(!st[i])
{
prime[cnt++] = i;
}
for(int j = 0; j < cnt && prime[j] <= n/i; ++ j)
{
st[i*prime[j]] = true;
if(i%prime[j]==0) break; //最小质因子筛
}
}
}
//数n!中含有多少个因子p
int get(int n, int p)
{
int ans = 0;
for(int t = p; t <= n; t = t*p) //不会爆
{
ans += n/t;
}
return ans;
}
vector mul(vector a, int b)
{
vector ans;
int r = 0;
for(int i = 0; i < a.size(); ++ i)
{
r = a[i]*b+r; //注意不能结果直接存到ans[i]
ans.push_back(r%10);
r /= 10;
}
while(r > 0)
{
ans.push_back(r%10);
r /= 10;
}
return ans;
}
int main()
{
cin >> a >> b;
get_primes(a);
for(int i = 0; i < cnt; ++ i)
{
sum[i] = get(a, prime[i]) - get(b, prime[i]) - get(a-b, prime[i]);
}
vector ans = {1}; //倒着存
for(int i = 0; i < cnt; ++ i)
{
for(int j = 0; j < sum[i]; ++ j)
{
ans = mul(ans, prime[i]);
}
}
for(int i = ans.size()-1; i >= 0; -- i)
{
cout << ans[i];
}
cout << endl;
return 0;
}
889.满足条件的01序列
#include
#include
#include
using namespace std;
typedef long long LL;
const int MOD = 1e9+7;
int n;
int qmi(int a, int b)
{
int ans = 1;
while(b)
{
if(b&1) ans = (LL)ans*a%MOD;
b >>= 1;
a = (LL)a*a%MOD;
}
return ans;
}
int main()
{
cin >> n;
int ans = 1; //卡特兰数公式:C(2n, n)-C(an, n-1) == C(2n, n)/(n+1)
for(int i = 1; i <= 2*n; ++ i)
{
ans = (LL)ans*i%MOD;
}
int tp = 1;
for(int i = 1; i <= n; ++ i)
{
tp = (LL)tp*i%MOD;
}
ans = (LL)ans*qmi(tp, MOD-2)%MOD;
ans = (LL)ans*qmi(tp, MOD-2)%MOD;
ans = (LL)ans*qmi(n+1, MOD-2)%MOD;
cout << ans << endl;
return 0;
}
890.能被整除的数
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 17;
int n, m;
int prime[N];
int main()
{
cin >> n >> m;
for(int i = 0; i < m; ++ i)
{
cin >> prime[i];
}
int ans = 0;
for(int i = 1; i < 1<>j&1)
{
if((LL)t*prime[j] <= n)
{
cnt ++;
t = t*prime[j];
}
else
{
yes = false;
break;
}
}
}
if(yes)
{
if(cnt%2==1)
{
ans += n/t;
}
else
{
ans -= n/t;
}
}
}
cout << ans << endl;
return 0;
}
891.Nim游戏
#include
#include
#include
using namespace std;
int n;
int main()
{
cin >> n;
int ans = 0;
for(int i = 1; i <= n; ++ i)
{
int x;
cin >> x;
ans ^= x;
}
if(ans!=0)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
return 0;
}
892.台阶-Nim游戏
#include
#include
#include
using namespace std;
int n;
int main()
{
cin >> n;
int ans = 0;
for(int i = 1; i <= n; ++ i)
{
int x;
cin >> x;
if(i%2==1)
{
ans ^= x;
}
}
if(ans != 0)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
return 0;
}
893.集合-Nim游戏*
#include
#include
#include
#include
#include
using namespace std;
const int N = 105, M = 10005;
int k, n;
int s[N];
int dp[M];
int sg(int x)
{
if(dp[x] != -1) return dp[x];
set S; //求相邻的sg
for(int i = 1; i <= k; ++ i)
{
if(x >= s[i])
{
S.insert(sg(x-s[i]));
}
}
int i = 0;
while(S.count(i)!=0)
{
i ++;
}
return dp[x] = i;
}
int main()
{
cin >> k;
for(int i = 1; i <= k; ++ i)
{
cin >> s[i];
}
cin >> n;
int ans = 0;
memset(dp, -1, sizeof dp); //求dp
for(int i = 1; i <= n; ++ i)
{
int x;
cin >> x;
ans = ans^sg(x);
}
if(ans!=0)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
return 0;
}
894.拆分-Nim游戏**
#include
#include
#include
#include
#include
using namespace std;
const int N = 105;
int n;
int dp[N];
int sg(int x)
{
if(dp[x] != -1) return dp[x];
set S; //存放可达的状态的sg值
for(int i = 0; i < x; ++ i)
{
for(int j = 0; j <= i; ++ j)
{
S.insert(sg(i)^sg(j)); //状态拆分求sg需要异或
}
}
int i = 0; //状态选择求sg需要nem
while(S.count(i))
{
i ++;
}
return dp[x] = i;
}
int main()
{
cin >> n;
int ans = 0;
memset(dp, -1, sizeof dp);
for(int i = 1; i <= n; ++ i)
{
int x;
cin >> x;
ans ^= sg(x);
}
if(ans!=0)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
return 0;
}
第五章 动态规划
2.01背包
#include
#include
#include
using namespace std;
const int N = 1005;
int n, m;
int v[N], w[N];
int dp[2][N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i)
{
cin >> v[i] >> w[i];
}
for(int i = 1; i <= n; ++ i)
{
for(int j = 0; j <= m; ++ j)
{
dp[i%2][j] = dp[(i-1)%2][j]; //不选
if(j >= v[i]) //可以选
{
dp[i%2][j] = max(dp[i%2][j], dp[(i-1)%2][j-v[i]]+w[i]);
}
}
}
cout << dp[n%2][m] << endl;
}
3.完全背包问题
#include
#include
#include
using namespace std;
const int N = 1005;
int n, m;
int v[N], w[N];
int dp[2][N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i)
{
cin >> v[i] >> w[i];
}
for(int i = 1; i <= n; ++ i)
{
for(int j = 0; j <= m; ++ j)
{
dp[i%2][j] = dp[(i-1)%2][j];
if(j >= v[i]) dp[i%2][j] = max(dp[i%2][j], dp[i%2][j-v[i]]+w[i]);
}
}
cout << dp[n%2][m] << endl;
return 0;
}
4.多重背包问题I
#include
#include
#include
using namespace std;
const int N = 105;
int v[N], w[N], s[N];
int n, m;
int dp[2][N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i)
{
cin >> v[i] >> w[i] >> s[i];
}
for(int i = 1; i <= n; ++ i)
{
for(int j = 0; j <= m; ++ j)
{
dp[i%2][j] = dp[(i-1)%2][j];
for(int k = 1; k <= s[i] && k*v[i] <= j; ++ k)
{
dp[i%2][j] = max(dp[i%2][j], dp[(i-1)%2][j-k*v[i]]+k*w[i]);
}
}
}
cout << dp[n%2][m] << endl;
return 0;
}
5.多重背包问题II*
#include
#include
#include
using namespace std;
const int N = 1005*15, M = 2005;
int n, m;
int v[N], w[N];
int dp[2][M];
int main()
{
cin >> n >> m;
int cnt = 0;
for(int i = 1; i <= n; ++ i)
{
int a, b, c;
cin >> a >> b >> c;
int t = 1;
while(t <= c)
{
cnt ++;
v[cnt] = a*t;
w[cnt] = b*t;
c -= t;
t = t*2;
}
if(c > 0)
{
cnt ++;
v[cnt] = a*c;
w[cnt] = b*c;
}
}
for(int i = 1; i <= cnt; ++ i)
{
for(int j = 0; j <= m; ++ j)
{
dp[i%2][j] = dp[(i-1)%2][j];
if(v[i] <= j)
{
dp[i%2][j] = max(dp[i%2][j], dp[(i-1)%2][j-v[i]]+w[i]);
}
}
}
cout << dp[cnt%2][m] << endl;
return 0;
}
9.分组背包问题
#include
#include
#include
using namespace std;
const int N = 105;
int n, m;
int s[N];
int v[N][N], w[N][N];
int dp[2][N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i)
{
cin >> s[i];
for(int j =1; j <= s[i]; ++ j)
{
cin >> v[i][j] >> w[i][j];
}
}
for(int i = 1; i <= n; ++ i)
{
for(int j = 0; j <= m; ++ j)
{
//不选
dp[i%2][j] = dp[(i-1)%2][j];
for(int k = 1; k <= s[i]; ++ k)
{
if(v[i][k] <= j)
{
dp[i%2][j] = max(dp[i%2][j], dp[(i-1)%2][j-v[i][k]]+w[i][k]);
}
}
}
}
cout << dp[n%2][m] << endl;
return 0;
}
898.数字三角形
#include
#include
#include
using namespace std;
const int N = 505, INF = 0x3f3f3f3f;
int n;
int g[N][N], dp[N][N];
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= i; ++ j)
{
cin >> g[i][j];
}
}
dp[1][1] = g[1][1];
for(int i = 2; i <= n; ++ i)
{
for(int j = 1; j <= i; ++ j)
{
if(j == 1)
{
dp[i][j] = dp[i-1][j] + g[i][j];
}
else if(i == j)
{
dp[i][j] = dp[i-1][j-1] + g[i][j];
}
else
{
dp[i][j] = max(dp[i-1][j-1], dp[i-1][j])+g[i][j];
}
}
}
int ans = -INF;
for(int i = 1; i <= n; ++ i)
{
ans = max(ans, dp[n][i]);
}
cout << ans << endl;
return 0;
}
895.最长上升子序列
#include
#include
#include
using namespace std;
const int N = 1005;
int n;
int num[N];
int dp[N];
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
cin >> num[i];
}
for(int i = 1; i <= n; ++ i)
{
dp[i] = 1;
for(int j = 1; j < i; ++ j)
{
if(num[j] < num[i])
{
dp[i] = max(dp[i], dp[j]+1);
}
}
}
int ans = 0;
for(int i = 1; i <= n; ++ i)
{
ans = max(ans, dp[i]);
}
cout << ans << endl;
return 0;
}
896.最长上升子序列II
#include
#include
#include
using namespace std;
const int N = 1e5+5, INF = 0x3f3f3f3f;
int n;
int num[N];
//dp[i]表示所有长度为i的上升子序列中的最小结尾,递增
int dp[N], cnt;
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
cin >> num[i];
}
dp[0] = -INF; //哨兵
for(int i = 1; i <= n; ++ i)
{
int l = 0, r = cnt;
while(l < r) //寻找严格小于num[i]的最大值的后一个
{
int mid = l+r+1>>1;
if(dp[mid] < num[i]) l = mid;
else r = mid-1;
}
dp[l+1] = num[i];
cnt = max(cnt, l+1);
}
cout << cnt << endl;
return 0;
}
897.最长公共子序列*
#include
#include
#include
#include
using namespace std;
const int N = 1005;
int n, m;
string a, b;
int dp[N][N]; //从a的前i个中选,b的前j个中选,最大长度
int main()
{
cin >> n >> m;
cin >> a >> b;
a = " " + a;
b = " " + b;
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= m; ++ j)
{
if(a[i] == b[j])
{
dp[i][j] = dp[i-1][j-1]+1;
}
else
{
dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
}
}
}
cout << dp[n][m] << endl;
return 0;
}
902.最短编辑距离*
#include
#include
#include
#include
using namespace std;
const int N = 1005;
int n, m;
string a, b;
int dp[N][N]; //a的前i个字母与b的前j个字母
int main()
{
cin >> n >> a >> m >> b;
a = " " + a;
b = " " + b;
dp[0][0] = 0;
for(int i = 1; i <= n; ++ i)
{
dp[i][0] = i;
}
for(int j = 1; j <= m; ++ j)
{
dp[0][j] = j;
}
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= m; ++ j)
{
dp[i][j] = min(dp[i-1][j], dp[i][j-1])+1;
if(a[i] == b[j])
{
dp[i][j] = min(dp[i][j], dp[i-1][j-1]);
}
else
{
dp[i][j] = min(dp[i][j], dp[i-1][j-1]+1);
}
}
}
cout << dp[n][m] << endl;
return 0;
}
899.编辑距离*
#include
#include
#include
#include
using namespace std;
const int N = 1005;
int n, m;
string s[N];
int dp[N][N];
int getDistance(string a, string b)
{
//memset初始化可能会超时
dp[0][0] = 0;
for(int i = 1; i < a.size(); ++ i)
{
dp[i][0] = i;
}
for(int j = 1; j < b.size(); ++ j)
{
dp[0][j] = j;
}
for(int i = 1; i < a.size(); ++ i)
{
for(int j = 1; j < b.size(); ++ j)
{
dp[i][j] = min(dp[i-1][j], dp[i][j-1])+1; //避免了被原来的dp[i][j]影响
if(a[i] == b[j])
{
dp[i][j] = min(dp[i][j], dp[i-1][j-1]);
}
else
{
dp[i][j] = min(dp[i][j], dp[i-1][j-1]+1);
}
}
}
return dp[a.size()-1][b.size()-1];
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i)
{
cin >> s[i];
s[i] = " " + s[i];
}
while(m --)
{
string c;
int t;
cin >> c >> t;
c = " " + c;
int cnt = 0;
for(int i = 1; i <= n; ++ i)
{
if(getDistance(s[i], c) <= t)
{
cnt ++;
}
}
cout << cnt << endl;
}
return 0;
}
282.石子合并
#include
#include
#include
using namespace std;
const int N = 305, INF = 0x3f3f3f3f;
int n;
int sum[N];
int dp[N][N];
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
cin >> sum[i];
sum[i] += sum[i-1];
}
for(int len = 2; len <= n; ++ len) //先算小区间的dp
{
for(int i = 1; i+len-1 <= n; ++ i)
{
int j = i+len-1;
dp[i][j] = INF;
for(int k = i; k < j; ++ k) //枚举分割的左区间右端点
{
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+1][j]);
}
dp[i][j] += sum[j]-sum[i-1];
}
}
cout << dp[1][n] << endl;
return 0;
}
900.整数划分
#include
#include
#include
using namespace std;
const int N = 1005, MOD = 1e9+7;
int n;
int dp[N][N];
int main()
{
cin >> n;
dp[0][0] = 1;
for(int i = 1; i <= n; ++ i)
{
for(int j = 0; j <= n; ++ j)
{
dp[i][j] = dp[i-1][j];
if(j >= i)
{
dp[i][j] += dp[i][j-i];
dp[i][j] %= MOD;
}
}
}
cout << dp[n][n] << endl;
return 0;
}
338.计数问题***
#include
#include
#include
#include
using namespace std;
//计算整数n有多少位
int dgt(int n)
{
int res = 0;
while(n) ++ res, n /= 10;
return res;
}
//计算1~n的整数中数字i出现的次数
int cnt(int n, int i)
{
int res = 0, d = dgt(n);
for(int j = 1; j <= d; ++ j) //从右到左的第j位数字i出现的次数
{
//l和r是左右两边的整数,dj是第j位的数字
int p = pow(10, j-1), l = n/p/10, r = n%p, dj = n/p%10;
if(i != 0)
{
res += l*p; //左边为000~abc-1
}
else if(i == 0 && l != 0) //左边不能全为0
{
res += (l-1)*p; //左边为001~abc-1
}
//计算第j位左边的整数等于l的情况
if((dj>i)&&(i||l)) //注意前缀不能都是0
{
res += p;
}
else if((dj==i)&&(i||l))
{
res += r+1; //0~r
}
}
return res;
}
int main()
{
int a, b;
while(cin >> a >> b, a||b)
{
if(a > b) swap(a, b);
for(int i = 0; i <= 9; ++ i)
{
cout << cnt(b, i) - cnt(a-1, i) << " ";
}
cout << endl;
}
return 0;
}
291.蒙德里安的梦想**
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 12, M = 1< stateFrom[M]; //state[i][j]表示状态i可以从状态j转移过来
LL dp[N][M]; //查看第i列,在i-1列插入到i列的状态为j的情况下的方案
int main()
{
while(cin >> n >> m, n||m)
{
for(int i = 0; i < 1<>j&1)==0)
{
cnt ++;
}
else
{
if(cnt%2==1)
{
flag = false;
break;
}
cnt = 0;
}
}
if(cnt%2==1)
{
flag = false;
}
st[i] = flag;
}
//再筛选出状态i可以从哪些状态转移过来
//也就是i-2列状态j插到i-1列状态i之后i-1列是否满足要求
for(int i = 0; i < 1<
91.最短Hamilton路径*
#include
#include
#include
#include
using namespace std;
const int N = 22, M = 1<<21;
int n;
int dist[N][N];
//dp[i][j]表示当前经过的点的状态为i,现在在j这个点的路径长度
int dp[M][N];
int main()
{
cin >> n;
for(int i = 0; i < n; ++ i)
{
for(int j = 0; j < n; ++ j)
{
cin >> dist[i][j];
}
}
memset(dp, 0x3f, sizeof dp);
dp[1][0] = 0;
for(int i = 0; i < 1<>j&1) //要保证经过的路径有这个点
{
//枚举中继节点
for(int k = 0; k < n; ++ k)
{
if(i>>k&1)
{
dp[i][j] = min(dp[i][j], dp[i-(1<
285.没有上司的舞会
#include
#include
#include
#include
using namespace std;
//有向边
const int N = 6005, M = N;
int h[N], e[M], ne[M], idx;
void init()
{
memset(h, -1, sizeof h);
}
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
int w[N]; //注意这里是点的权值,不是边的
bool st[N]; //表示有父节点
int n;
int dp[N][2];
//求dp[root][0]和dp[root][1]
void dfs(int root)
{
//初始化
dp[root][0] = 0;
dp[root][1] = w[root];
for(int i = h[root]; ~i; i=ne[i])
{
int j = e[i];
dfs(j);
dp[root][0] += max(dp[j][0], dp[j][1]);
dp[root][1] += dp[j][0];
}
}
int main()
{
cin >> n;
init();
for(int i = 1; i <= n; ++ i)
{
int t;
cin >> t;
w[i] = t;
}
for(int i = 1; i < n; ++ i)
{
int a, b;
cin >> a >> b;
add(b, a);
st[a] = true;
}
int root = -1;
for(int i = 1; i <= n; ++ i)
{
if(!st[i])
{
root = i;
break;
}
}
dfs(root);
cout << max(dp[root][0], dp[root][1]) << endl;
return 0;
}
901.滑雪
#include
#include
#include
#include
using namespace std;
const int N = 305;
int n, m;
int g[N][N];
//f[i][j]表示从i,j开始走过的最长长度,0表示未计算,至少为1
int f[N][N];
int dp(int x, int y)
{
if(f[x][y]) return f[x][y];
f[x][y] = 1;
int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};
for(int i = 0; i < 4; ++ i)
{
int a = x+dx[i], b = y+dy[i];
if(a<=0 || a>n || b<=0 || b>m || g[a][b]>=g[x][y])
{
continue;
}
f[x][y] = max(f[x][y], dp(a, b)+1);
}
return f[x][y];
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= m; ++ j)
{
cin >> g[i][j];
}
}
int res = 0;
for(int i = 1; i <= n; ++ i)
{
for(int j = 1; j <= m; ++ j)
{
res = max(res, dp(i, j));
}
}
cout << res << endl;
return 0;
}
第六章 贪心
905.区间选点
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n;
struct Range{
int l, r;
bool operator<(const Range &ra) const{
return r<=ra.r;
}
}range[N];
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
cin >> range[i].l >> range[i].r;
}
sort(range+1, range+n+1);
int ans = 1;
int lastNode = range[1].r;
for(int i = 2; i <= n; ++ i)
{
if(range[i].l <= lastNode)
{
continue;
}
else
{
lastNode = range[i].r;
ans ++;
}
}
cout << ans << endl;
return 0;
}
908.最大不相交区间数量
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n;
struct Range{
int l, r;
bool operator<(const Range &ra) const{
if(r != ra.r)
return rra.l;
}
}range[N];
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
cin >> range[i].l >> range[i].r;
}
sort(range+1, range+n+1);
int ans = 1;
int R = range[1].r;
for(int i = 2; i <= n; ++ i)
{
if(range[i].l > R)
{
ans ++;
R = range[i].r;
}
}
cout << ans << endl;
return 0;
}
906.区间分组*
#include
#include
#include
#include
#include
#define l first
#define r second
using namespace std;
typedef pair PII;
int n;
priority_queue, greater> heap; //组内最大右端点从小到大排序
vector range;
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int l, r;
cin >> l >> r;
range.push_back({l, r});
}
sort(range.begin(), range.end());
heap.push(range[0].r);
for(int i = 1; i < range.size(); ++ i)
{
int R = heap.top();//小根堆
if(range[i].l <= R) //比右端点最小的组的右端点还要小
{
heap.push(range[i].r);
}
else
{
if(range[i].r > R)
{
heap.pop();
heap.push(range[i].r);
}
}
}
cout << heap.size() << endl;
return 0;
}
907.区间覆盖*
#include
#include
#include
using namespace std;
const int N = 1e5+5, INF = 0x3f3f3f3f;
int n;
int start, tail;
struct Range{
int l, r;
bool operator<(const Range &Ra) const{
return l <= Ra.l;
}
}range[N];
int main()
{
cin >> start >> tail;
cin >> n;
for(int i = 0; i < n; ++ i)
{
cin >> range[i].l >> range[i].r;
}
sort(range, range+n);
int ans = 0;
bool success = false;
for(int i = 0; i < n; ++ i) //枚举区间
{
int j = i;
int maxR = -INF;
while(j < n && range[j].l <= start)
{
maxR = max(maxR, range[j].r);
++ j;
}
if(maxR <= start)
{
success = false;
break;
}
start = maxR;
ans ++;
if(maxR >= tail)
{
success = true;
break;
}
i = j-1;
}
if(success)
{
cout << ans << endl;
}
else
{
cout << -1 << endl;
}
return 0;
}
148.合并果子
#include
#include
#include
#include
using namespace std;
int n;
priority_queue, greater> heap; //小根堆
int main()
{
cin >> n;
for(int i = 1; i <= n; ++ i)
{
int x;
cin >> x;
heap.push(x);
}
int ans = 0;
while(heap.size() != 1)
{
int a1 = heap.top();
heap.pop();
int a2 = heap.top();
heap.pop();
ans += a1+a2;
heap.push(a1+a2);
}
cout << ans << endl;
return 0;
}
913.排队打水
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 1e5+5;
int n;
int tim[N];
int main()
{
cin >> n;
for(int i = 0; i < n; ++ i)
{
cin >> tim[i];
}
sort(tim, tim+n);
reverse(tim, tim+n); //从大到小排序
LL ans = 0;
for(int i = n-1; i >= 0; -- i)
{
ans += (LL)tim[i]*i;
}
cout << ans << endl;
return 0;
}
104.货仓选址
#include
#include
#include
using namespace std;
const int N = 1e5+5;
int n;
int a[N];
int main()
{
cin >> n;
for(int i = 0; i < n; ++ i)
{
cin >> a[i];
}
sort(a, a+n);
int ans = 0;
for(int i = 0, j = n-1; i < j; ++ i, -- j)//if i==j说明货仓在这里,为0
{
ans += (a[j]-a[i]);
}
cout << ans << endl;
return 0;
}
125.耍杂技的牛*
#include
#include
#include
#define x first
#define y second
using namespace std;
typedef pair PII;
const int N = 50005, INF = 0x3f3f3f3f;
int n;
PII cow[N];
int main()
{
cin >> n;
for(int i = 0; i < n; ++ i)
{
int w, s;
cin >> w >> s;
cow[i]= {w+s, w};
}
sort(cow, cow+n);
int ans = -INF;
int sum = 0; //sum_w
for(int i = 0; i < n; ++ i)
{
int w = cow[i].y, s = cow[i].x - cow[i].y;
ans = max(ans, sum-s);
sum += w;
}
cout << ans << endl;
return 0;
}