【算法1-1】模拟与高精度
https://www.luogu.com.cn/training/106#problems
P1518 [USACO2.4]两只塔姆沃斯牛 The Tamworth Two
https://www.luogu.com.cn/problem/P1518
原本我是单纯的模拟,没有判断死循环即无解的情况。看了下面的分析,给t设置一个最大值,超过就无解。
“看到题后,我们的第一反应是寻找环判重叠来算次数,但这样也太麻烦了,所以考虑更简洁的直接模拟:
由于只有10x10行的地图,因此牛和农夫最多只有400种情况(每个位置有4次,4方向各一次)由乘法原理可知,无论怎么走,最多都只能出现160000种情况,实际上还要小的多(而且160000也非常小了)”
#include <bits/stdc++.h>
using namespace std;
const int N = 12;
char a[N][N];
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
int main(){
int n, sx, sy, ex, ey;
for(int i = 0; i < 10; i++){
for(int j = 0; j < 10; j++) {
cin >> a[i][j];
if(a[i][j] == 'F') sx = i, sy = j;
if(a[i][j] == 'C') ex = i, ey = j;
}
}
int t = 0, d1 = 0, d2 = 0;
while((sx != ex || sy != ey ) && t < 200000){
t++;
//对于F
int x = sx + dx[d1], y = sy + dy[d1];
if(x >= 0 && x < 10 && y >= 0 && y < 10 && a[x][y] != '*') sx = x, sy = y;
else d1 = (d1 + 1) % 4;
//对于C
x = ex + dx[d2], y = ey + dy[d2];
if(x >= 0 && x < 10 && y >= 0 && y < 10 && a[x][y] != '*') ex = x, ey = y;
else d2 = (d2 + 1) % 4;
//printf("sx = %d sy = %d ex = %d ey = %d\n");
}
if(t == 200000) printf("%d\n", 0);
else printf("%d\n", t);
//cout << p[pos].w << endl;
return 0;
}P1098 [NOIP2007 提高组] 字符串的展开
https://www.luogu.com.cn/problem/P1098
难点在于处理各种特殊情况,要仔细。
#include <bits/stdc++.h>
using namespace std;
int main(){
int p1, p2, p3;
cin >> p1 >> p2 >> p3;
string s, ans = "";
cin >> s;
char last;
for(int i = 0; i < s.size(); i++) {
if(s[i] != '-') ans += s[i];
else if((i == 0 || i == s.size() - 1) && s[i] == '-') ans += '-';
else if(s[i]=='-' && (s[i - 1] == '-' || s[i + 1] == '-')) ans += '-';
else if(s[i] == '-'){
if(s[i - 1] >= s[i + 1] || (s[i - 1] < 58 && s[i + 1] > 96)) ans += '-';
else if(s[i + 1] == s[i - 1] + 1) continue;
else{
if(p1 == 3){
for(int j = 1; j <= (s[i+1] - s[i-1] - 1)* p2; j++)ans += '*';
}
else if(s[i+1] <= '9' && s[i+1] >= '0' && s[i-1] <= '9' && s[i-1] >= '0'){
if(p3 == 1){
//数字,顺序
for(int j = s[i-1]+1; j <= s[i+1]-1; j++){
for(int k = 1; k <= p2; k++)
ans = ans + (char)(j);
}
}else if(p3 == 2){
// 数字 ,逆序
for(int j = s[i+1]-1; j >= s[i-1]+1; j--){
for(int k = 1; k <= p2; k++)
ans = ans + (char)(j);
}
}
}
else if(p1 == 1){
if(p3 == 1){
//小写字母,顺序
for(int j = s[i-1]+1; j <= s[i+1]-1; j++){
for(int k = 1; k <= p2; k++)
ans = ans + (char)(j);
}
}else if(p3 == 2){
// 小写字母,逆序
for(int j = s[i+1]-1; j <= s[i-1]+1; j--){
for(int k = 1; k >= p2; k++)
ans = ans + (char)(j);
}
}
}
else if(p1 == 2) {
if(p3 == 1){
//大写字母,顺序
for(int j = s[i-1]+1; j <= s[i+1]-1; j++){
for(int k = 1; k <= p2; k++)
ans = ans + (char)(j - 'a' + 'A');
}
}else if(p3 == 2){
// 大写字母,逆序
for(int j = s[i + 1]-1; j >= s[i-1]+1; j--){
for(int k = 1; k <= p2; k++)
ans = ans + (char)(j - 'a' + 'A');
}
}
}
}
}
}
//printf("%d\n", t);
cout << ans << endl;
return 0;
}P1045 [NOIP2003 普及组] 麦森数
https://www.luogu.com.cn/problem/P1045
【算法1-2】排序
快排
P1177 【模板】快速排序
https://www.luogu.com.cn/problem/P1177
快排应用:第k个数 练习分治算法
P1923 【深基9.例4】求第 k 小的数
https://www.luogu.com.cn/problem/P1923
方法一:
这里用快排,先排序再输出q[k],不能完全通过,部分会TLE;
方法二:
修改一下快排函数,找到第k个就返回它。
而且这里用cin读入仍然会TLE
换成scanf就AC了。
模板的k是从1开始,这里k是从0开始,所以要quick_sort(a, 0, n-1, k + 1)
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 5000010;
int n, k;
int a[N];
int quick_sort(int q[], int l, int r, int k){
if(l >= r) return q[l];
int x = q[l + r >> 1];
int i = l - 1, j = r + 1;
while(i < j){
do i++; while(q[i] < x);
do j--; while(q[j] > x);
if(i < j){
int tmp = q[i];
q[i] = q[j];
q[j] = tmp;
}
}
if(j - l + 1 >= k) quick_sort(q, l, j, k);
else quick_sort(q, j + 1, r, k - (j - l + 1));
}
int main(){
int n;
scanf("%d %d", &n, &k);
for(int i = 0; i < n; i++){
scanf("%d", &a[i]);
}
//模板的k是从1开始,这里k是从0开始
printf("%d ", quick_sort(a, 0, n-1, k + 1));
return 0;
}方法三:
nth_element的用法
https://www.cnblogs.com/xenny/p/9424894.html
#include <bits/stdc++.h>
using namespace std;
const int N = 5000010;
int n, k;
int a[N];
int main(){
scanf("%d%d", &n, &k);
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
nth_element(a, a + k, a + n); //使第k小整数就位
printf("%d", a[k]); //调用第k小整数
return 0;
}冒泡排序
P1116 车厢重组
https://www.luogu.com.cn/problem/P1116
方法一: 冒泡排序
#include <bits/stdc++.h>
using namespace std;
const int N = 10010;
int n;
int a[N], cnt;
void bubble(){
for(int i = 0; i < n - 1; i++){
for(int j = 0; j < n - 1; j++){
if(a[j] > a[j + 1]){
swap(a[j], a[j + 1]);
cnt++;
}
}
}
}
int main(){
cin >> n;
for(int i = 0; i < n; i++) cin >> a[i];
bubble();
printf("%d\n", cnt);
return 0;
}方法二:题目只是问需要多少次移动,没问排序结果
没有做排序,只是迭代去计算每个数字前有几个数字比它大,这意味着它必须要移动几次。
#include <bits/stdc++.h>
using namespace std;
const int N = 10010;
int a[N];
int main(){
int n;
cin >> n;
for(int i = 0; i < n; i++) cin >> a[i];
int cnt = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < i; j++){
if(a[j] > a[i]) cnt++;
}
}
printf("%d\n", cnt);
return 0;
}P1012 [NOIP1998 提高组] 拼数
https://www.luogu.com.cn/problem/P1012
原本我的想法是大的数字放在高位,所以直接比较每个字符串大小就行,但是只能75分。
出错的例子为:
#include <bits/stdc++.h>
using namespace std;
const int N = 30;
int n;
string a[N];
bool cmp(string a, string b){
return a > b;
}
int main(){
cin >> n;
for(int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a, a + n, cmp);
string ans = "";
for(int i = 0; i < n; i++) {
//cout << a[i] << endl;
ans += a[i];
}
cout << ans << endl;
//printf("%.3lf\n", ans);
return 0;
}所以直接改cmp函数,100分AC
bool cmp(string a, string b){
return a + b > b + a;
}【算法1-3】暴力枚举
P1036 [NOIP2002 普及组] 选数
https://www.luogu.com.cn/problem/P1036
但其实下面代码中的st数组也可以不要,因为dfs每次传入了开始扫描的数组下标b就保证了不会重复访问。
#include <bits/stdc++.h>
using namespace std;
const int N = 30;
int n, k;
int a[N], st[N], cnt;
bool isPrime(int x){
for(int i = 2; i <= x / i; i++){
if(x % i == 0) return false;
}
return true;
}
void dfs(int x, int b, int sum){
if(x == k){
if(isPrime(sum)) {
//cout << sum << endl;
cnt++;
}
return ;
}
for(int i = b; i < n; i++){
if(!st[i]){
st[i] = 1;
//cout << "sum = " << sum << "ai = " << a[i] << endl;
dfs(x + 1, i + 1, sum + a[i]);
st[i] = 0;
}
}
}
int main(){
cin >> n >> k;
for(int i = 0; i < n; i++) cin >> a[i];
dfs(0, 0, 0);
cout << cnt << endl;
return 0;
}P1157 组合的输出
都用了dfs,但题目要求的排列不同,所以dfs要相应调整。
P3392 涂国旗
https://www.luogu.com.cn/problem/P3392
先做一下预处理,求出每行每个色块的个数
让后两个嵌套的for循环分别模拟白色行数(顶部)和红色行数(底部)
进而求出用这种方法涂制国旗所需次数,再来个很简单的判断,就可以得出最少次数
#include <bits/stdc++.h>
using namespace std;
const int N = 60;
int n, m;
char a[N][N];
int W[N], B[N], R[N];
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++){
cin >> a[i][j];
if(a[i][j] == 'W') W[i]++;
if(a[i][j] == 'B') B[i]++;
if(a[i][j] == 'R') R[i]++;
}
}
int ans = 2e9;
//最上方变为白的行数
for(int i = 1; i <= n - 2; i++) {
//最下方变为红的行数
for(int j = 1; j <= n - 2; j++) {
int tmp = 0;
if(i + j > n - 1) continue; //剩余蓝行不足1
//变白格子需要的次数
for(int k = 1; k <= i; k++) tmp += B[k] + R[k];
//变蓝格子需要的次数
for(int k = i + 1; k <= n - j; k++) tmp += W[k] + R[k];
//变红格子需要的次数
for(int k = n - j + 1; k <= n; k++) tmp += W[k] + B[k];
ans = min(ans, tmp);
}
}
cout << ans << endl;
return 0;
}P3654 First Step (ファーストステップ)
https://www.luogu.com.cn/problem/P3654
每次达到k,t--而不是直接变为0,类似滑动窗口。
注意k=1的特殊情况,行列会分别算一遍所以要除以2.
P1149 [NOIP2008 提高组] 火柴棒等式
https://www.luogu.com.cn/problem/P1149
关键是要确定数的范围,这里要是不太能确定的话可以保守一点取大一点。我是看了题解,发现他们的数在2000左右。
P3799 妖梦拼木棒
https://www.luogu.com.cn/problem/P3799
参考第一篇题解
https://www.luogu.com.cn/problem/solution/P3799
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10, mod = 1e9 + 7;
int n, maxl;
int a[N], cnt[N];
//求得从a个数中取出2个数的组合
int C2(int a){
return a * (a - 1) / 2 % mod;
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
maxl = max(maxl, a[i]);
cnt[a[i]] ++;
}
int ans = 0;
//枚举a,b
for(int i = 1; i <= maxl; i++) {
for(int j = i; j <= maxl; j++) {
if(i == j) ans = (ans + C2(cnt[i]) * C2(cnt[i + j])) % mod;
else ans = (ans + cnt[i] * cnt[j] % mod * C2(cnt[i + j]) % mod) % mod;
}
}
cout << ans << endl;
return 0;
}【算法1-4】递推与递归
P1255 数楼梯
https://www.luogu.com.cn/problem/P1255
这里要用到高精度,数值比较大。
vector<vector<int> >的各种用法和易错
https://www.cnblogs.com/superjn/p/10730541.html</int>
#include <bits/stdc++.h>
using namespace std;
int n;
vector<vector<int> > f;
vector<int> add(vector<int> &a, vector<int> &b){
if(a.size() < b.size()) return add(b, a);
vector<int> c;
int t = 0;
for(int i = 0; i < a.size(); i++){
t += a[i];
if(i < b.size()) t += b[i];
c.push_back(t % 10);
t /= 10;
}
if(t) c.push_back(t);
return c;
}
int main(){
cin >> n;
vector<int> t;
t.push_back(0); f.push_back(t);
t.pop_back(); t.push_back(1); f.push_back(t);
t.pop_back(); t.push_back(2); f.push_back(t);
for(int i = 3; i <= n; i++) {
vector<int> tmp = add(f[i - 1], f[i - 2]);
f.push_back(tmp);
}
for(int i = f[n].size() - 1; i >= 0; i--)
cout << f[n][i];
return 0;
}P1002 [NOIP2002 普及组] 过河卒
https://www.luogu.com.cn/problem/P1002
这里需要知道马是怎么走的,且马能一步走到的地方是不能经过的。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 30;
int n, m, mx, my;
int dx[9] = {0, -2, -1, 1, 2, 2, 1, -1, -2};
int dy[9] = {0, 1, 2, 2, 1, -1, -2, -2, -1};
LL f[N][N];
bool st[N][N]; //是否被马拦住
int main(){
cin >> n >> m >> mx >> my;
n++, m++, mx++, my++;
//标记不能经过的位置
for(int i = 0; i < 9; i++) st[mx + dx[i]][my + dy[i]] = 1;
f[1][1] = 1;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if((i != 1 || j != 1) && !st[i][j]) f[i][j] = f[i - 1][j] + f[i][j - 1];
}
}
cout << f[n][m] << endl;
return 0;
}P1044 [NOIP2003 普及组] 栈
https://www.luogu.com.cn/problem/P1044
https://www.luogu.com.cn/problem/solution/P1044
有多种方法。
P1928 外星密码
https://www.luogu.com.cn/problem/P1928
这个题解法很神奇,再看看。
P1164 小A点菜
https://www.luogu.com.cn/problem/P1164
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 110;
int m, n, cnt, t;
int a[N];
void dfs(int k, int sum){
t++;
if(sum == m) {
cnt++;
return;
}
if(k > n) return;
dfs(k + 1, sum);
dfs(k + 1, sum + a[k]);
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i];
dfs(1, 0);
cout << cnt << endl;
return 0;
}dfs会部分超时
改用dp,AC
#include <bits/stdc++.h>
using namespace std;
const int N = 110, M = 10010;
int m, n;
int a[N], f[M];
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i];
f[0] = 1;
for(int i = 1; i <= n; i++){
for(int j = m; j >= a[i]; j--){
f[j] = f[j] + f[j - a[i]];
}
}
cout << f[m] << endl;
return 0;
}P1990 覆盖墙壁
https://www.luogu.com.cn/problem/P1990
再看看
P3612 [USACO17JAN]Secret Cow Code S
重新写写!是参考题解AC的。
P1228 地毯填补问题
https://www.luogu.com.cn/problem/P1228
分治,重新写写!是参考题解AC的。
【算法1-5】贪心
P1803 凌乱的yyy / 线段覆盖
https://www.luogu.com.cn/problem/P1803
本来想的是按开始时间从小到大排,但WA
实际上应该按结束时间从小到大排,越早结束越应该选。
类似问题:
在一个数轴上有n条线段,现要选取其中k条线段使得这k条线段两两没有重合部分,问最大的k为多少。
最左边的线段放什么最好?
显然放右端点最靠左的线段最好,从左向右放,右端点越小妨碍越少
其他线段放置按右端点排序,贪心放置线段,即能放就放
本来直接用数组排序,每次计算两个数的值,还用到了前缀和
但只通过了一个,仔细思考后发现,把最小的两个相加后的值不一定是当前最小的,而这个题始终要选最小的两个相加。
所以要用小根堆维护,c++里由优先队列实现。
priority_queue<int, vector<int>, greater<int> > heap;</int></int>
P5019 [NOIP2018 提高组] 铺设道路
https://www.luogu.com.cn/problem/P5019
用模拟会超时。用贪心的思路求解。
再写一遍!
【算法1-6】二分查找与二分答案
P1102 A-B 数对
https://www.luogu.com.cn/problem/P1102
自己写的,本来只用了一个简单二分,但是数组可能有重复值,所以要先排序,然后再找出现的第一个位置和最后一个位置。
题解还有更简单的方法可以看看。
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
typedef long long LL;
const int N = 1000010, mod = 10000;
int n, c;
int a[N];
//找等于x的最小的位置
int search1(int x){
int l = 1, r = n;
while(l < r){
int mid = (l + r) / 2;
if(a[mid] >= x) r = mid;
else l = mid + 1;
}
if(a[l] == x) return l;
else return -1;
}
//找等于x的最大的位置
int search2(int x){
int l = 1, r = n;
while(l < r){
int mid = (l + r + 1) / 2;
if(a[mid] <= x) l = mid;
else r = mid - 1;
}
if(a[l] == x) return l;
else return -1;
}
int main(){
cin >> n >> c;
for(int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + 1 + n);//用二分数组要有序
LL cnt = 0;
//枚举b,a = b + c
for(int i = 1; i <= n; i++){
if(search2(a[i] + c) != -1 && search1(a[i] + c) != -1 && search2(a[i] + c) != i && search1(a[i] + c) != i)
cnt += search2(a[i] + c) - search1(a[i] + c) + 1;
}
cout << cnt << endl;
return 0;
}P1678 烦恼的高考志愿
https://www.luogu.com.cn/problem/P1678
再看看
P2678 [NOIP2015 提高组] 跳石头
P3853 [TJOI2007]路标设置
P1182 数列分段 Section II
P1163 银行贷款
P3743 kotori的设备
类似思路,思考什么样的问题可以用二分。
【算法1-7】搜索
P2392 kkksc03考前临时抱佛脚
重重重!
P1433 吃奶酪
常规的dfs+剪枝 最后一个测试点会TLE
查看题解
这里我没有像他那样先按距离排序,而是直接在源代码上加了一个超时弹出的条件,最后一个也能AC了。
void dfs(int now, int cnt, double d){
l++;
if(l >= 30000000) {
int t = clock();
if(t >= 940) return;
}
if(d >= ans) return;
if(cnt == n){
ans = min(ans, d);
return;
}
for(int i = 1; i <= n; i++){
if(now != i && !vis[i]){
vis[i] = 1;
//cout << dis[now][i]<< endl;
dfs(i, cnt + 1, d + dis[now][i]);
vis[i] = 0;
}
}
}P1101 单词方阵
和一般的dfs套路不一样,要多一步处理
P1162 填涂颜***r>https://www.luogu.com.cn/problem/P1162
本来用bfs找1围成的形状的上下左右四条边,但是只得了32分。因为围城的图形不是矩形,是不规则的,所以会有在围墙外的0被改为2.
可以转为思考哪些0应该被涂,哪些不应该被涂。
边界上的一定不会被涂,和它相连的也不应该被涂,剩下的就是被1围起来的。

京公网安备 11010502036488号