目录
1/4 (dfs暴力枚举)AcWing 2060. 奶牛选美
1/10 (差分+离散化)AcWing 1987. 粉刷栅栏
1/11 (前/后序最值)AcWing 1978. 奶牛过马路
1/14 (差分+离散化)AcWing 1952. 金发姑娘和 N 头牛
1/2 AcWing 2058. 笨拙的手指
输入样例:
1010
212
输出样例:
14
样例解释
14 在二进制下的正确表示为 1110,在三进制下的正确表示为 112。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>
using namespace std;
//秦九韶算法, k进制转十进制
int trans(string s, int k)
{
int res = 0;
int len = s.length();
for(int i = 0; i < len; i ++) res = res*k+s[i]-'0';
return res;
}
int main()
{
unordered_set<int> s;
string a, b;
cin >> a >> b;
for(auto& c: a){
c ^= 1; //48->49 49->48
s.insert(trans(a, 2));
c ^= 1;
}
for(auto& c: b){
char st = c;
for(int i = 0; i < 3; i ++){
if(i+'0'!=c){
c = i+'0';
int x = trans(b, 3);
if(s.count(x)){
cout << x << endl;
return 0;
}else{
s.insert(x);
}
}
c = st;
}
}
return 0;
}
1/3 (前缀和)AcWing 2041. 干草堆
输入样例:
7 4
5 5
2 4
4 6
3 5
输出样例:
1
样例解释
贝茜完成所有指令后,各堆高度为 0,1,2,3,3,1,0。
将各高度从小到大排序后,得到 0,0,1,1,2,3,3,位于中间的是 1。
前缀和
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6+10;
int s[N],a[N];
int main()
{
int n,t;
cin >> n >> t;
while(t --)
{
int l ,r;
cin >> l >> r;
s[l]++;
s[r+1]--;
}
for(int i = 1; i <= n; i++) s[i]+=s[i-1];
sort(s+1, s+n+1);
cout << s[(1+n)>>1];
return 0;
}
1/4 (dfs暴力枚举)AcWing 2060. 奶牛选美
听说最近两斑点的奶牛最受欢迎,约翰立即购进了一批两斑点牛。
不幸的是,时尚潮流往往变化很快,当前最受欢迎的牛变成了一斑点牛。
约翰希望通过给每头奶牛涂色,使得它们身上的两个斑点能够合为一个斑点,让它们能够更加时尚。
牛皮可用一个 N×M 的字符矩阵来表示,如下所示:
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....
其中,X 表示斑点部分。
如果两个 X 在垂直或水平方向上相邻(对角相邻不算在内),则它们属于同一个斑点,由此看出上图中恰好有两个斑点。
约翰牛群里所有的牛都有两个斑点。
约翰希望通过使用油漆给奶牛尽可能少的区域内涂色,将两个斑点合为一个。
在上面的例子中,他只需要给三个 .. 区域内涂色即可(新涂色区域用 ∗∗ 表示):
................
..XXXX....XXX...
...XXXX*...XX...
.XXXX..**..XXX..
........XXXXX...
.........XXX....
请帮助约翰确定,为了使两个斑点合为一个,他需要涂色区域的最少数量。
输入格式
第一行包含两个整数 N 和 M。
接下来 NN 行,每行包含一个长度为 M 的由 X 和 . 构成的字符串,用来表示描述牛皮图案的字符矩阵。
输出格式
输出需要涂色区域的最少数量。
数据范围
1≤N,M≤50
输入样例:
6 16
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....
输出样例:
3
数据范围较小, 可以dfs+暴力枚举, 取两区块之间最小曼哈顿距离
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 51;
typedef pair<int, int> PII;
int n, m;
char g[N][N];
vector<PII> v[2];
int dx[4]={0, 1, 0, -1}, dy[4]={1, 0, -1, 0};
void dfs(int x, int y, vector<PII>& u)
{
g[x][y] = '.';
u.push_back({x, y});
for(int i = 0; i < 4; i ++)
{
int nx=x+dx[i], ny=y+dy[i];
if(nx>=0 && nx<n && ny>=0 && ny<m && g[nx][ny]=='X') //周围所有X
dfs(nx, ny, u);
}
}
int main()
{
cin >> n >> m;
for(int i = 0; i < n; i ++) cin >> g[i];
for(int i = 0, k = 0; i < n; i ++)
for(int j = 0; j < m; j++)
if(g[i][j] =='X') dfs(i, j, v[k++]);
int res = 1e9;
for(auto& a: v[0])
for(auto& b : v[1])
res = min(res, abs(a.first-b.first)+abs(a.second-b.second)-1);
cout << res;
return 0;
}
1/5 (bfs)AcWing 2019. 拖拉机
干了一整天的活,农夫约翰完全忘记了他把拖拉机落在田地中央了。
他的奶牛非常调皮,决定对约翰来场恶作剧。
她们在田地的不同地方放了 N 捆干草,这样一来,约翰想要开走拖拉机就必须先移除一些干草捆。
拖拉机的位置以及 N 捆干草的位置都是二维平面上的整数坐标点。
拖拉机的初始位置上没有干草捆。
当约翰驾驶拖拉机时,他只能沿平行于坐标轴的方向(北,南,东和西)移动拖拉机,并且拖拉机必须每次移动整数距离。
例如,驾驶拖拉机先向北移动 2单位长度,然后向东移动 3 单位长度。
拖拉机无法移动到干草捆占据的位置。
请帮助约翰确定他需要移除的干草捆的最小数量,以便他能够将拖拉机开到二维平面的原点。
输入格式
第一行包含三个整数:N 以及拖拉机的初始位置 (x,y)。
接下来 N 行,每行包含一个干草捆的位置坐标 (x,y)。
输出格式
输出约翰需要移除的干草捆的最小数量。
数据范围
1≤N≤50000,
1≤x,y≤1000
输入样例:
7 6 3
6 2
5 2
4 3
2 1
7 3
5 4
6 4
输出样例:
1
双端队列广搜(可以看做简化版的dijskral)
任意时刻双端队列中的点到目标点的距离最多分为两种(小在前,大在后)
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 1005;
typedef pair<int, int> PII;
int x0, y0, t;
bool st[N][N];
int dist[N][N],g[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int bfs()
{
memset(dist, 0x3f, sizeof(dist));
deque<PII> q;
q.push_back({x0, y0});
dist[x0][y0]=0;
while(q.size())
{
auto t = q.front();
q.pop_front();
int x = t.first, y = t.second;
st[x][y]=true;
for(int i = 0; i < 4; i ++)
{
int nx = x+dx[i], ny = y+dy[i];
if(nx>=0 && nx<N && ny>=0 && ny<N)
{
if(dist[nx][ny] > dist[x][y]+g[nx][ny])
{
dist[nx][ny]=dist[x][y]+g[nx][ny];
if(g[nx][ny]) q.push_back({nx, ny});
else q.push_front({nx, ny});
}
}
}
}
return dist[0][0];
}
int main()
{
cin >> t >> x0 >> y0;
int x, y;
while(t --)
{
scanf("%d%d", &x, &y);
g[x][y] = 1;
}
cout << bfs();
return 0;
}
优化一下bfs函数的细节,根据访问状态和当前位置跳过流程
int bfs()
{
memset(dist, 0x3f, sizeof(dist));
deque<PII> q;
q.push_back({x0, y0});
dist[x0][y0]=0;
while(q.size())
{
auto t = q.front();
q.pop_front();
int x = t.first, y = t.second;
if(!x && !y) break;
if(st[x][y]) continue;
st[x][y]=true;
for(int i = 0; i < 4; i ++)
{
int nx = x+dx[i], ny = y+dy[i];
if(nx>=0 && nx<N && ny>=0 && ny<N)
{
if(dist[nx][ny] > dist[x][y]+g[nx][ny])
{
dist[nx][ny]=dist[x][y]+g[nx][ny];
if(g[nx][ny]) q.push_back({nx, ny});
else q.push_front({nx, ny});
}
}
}
}
return dist[0][0];
}
快了不少
1/6 (贪心)AcWing 2014. 岛
每当下雨时,农夫约翰的田地总是被洪水淹没。
由于田地不是完全水平的,所以一些地方充满水后,留下了许多被水隔开的“岛”。
约翰的田地被描述为由 N 个连续高度值 H1,…,HN指定的一维场景。
假设该场景被无限高的围墙包围着,请考虑暴雨期间发生的情况:
最低处首先被水覆盖,形成一些不连贯的岛,随着水位的不断上升,这些岛最终都会被覆盖。
一旦水位等于一块田地的高度,那块田地就被认为位于水下。
上图显示了一个示例:在左图中,我们只加入了刚好超过 1 单位的水,此时剩下 4 个岛(最大岛屿剩余数量),而在右图中,我们共加入了 7 单位的水,此时仅剩下 2 个岛。
请计算,暴风雨期间我们能在某个时间点看到的最大岛屿数量。
水会一直上升到所有田地都在水下。
输入格式
第一行包含整数 N。
接下来 N 行,每行包含一个整数表示 Hi。
输出格式
输出暴风雨期间我们能在某个时间点看到的最大岛屿数量。
数据范围
1≤N≤10^5,
1≤Hi≤10^9
贪心法
只有以上两种情况下,当中间的区域要被淹没时,总的岛屿数会变化
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
struct land {
int x, h;
bool operator < (const land& o)
{
if (h!=o.h)
return h < o.h;
else
return x < o.x;
}
} d[N];
bool is_down[N];
int n;
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; ++i){
d[i].x = i;
scanf("%d", &d[i].h);
}
sort(d, d + n);
int cur = 1, res = 1;
for (int i = 0; i < n; ++i)
{
int x = d[i].x;
is_down[x] = 1;
bool left = x > 0 && !is_down[x - 1]; //左岛是否还在
bool right = x < n - 1 && !is_down[x + 1]; //右岛是否还在
if (left && right) cur++;
else if (!left && !right) cur--;
if (d[i + 1].h != d[i].h)
res = max(cur, res);
}
cout << res;
}
1/7 (DFS)AcWing 2005. 马蹄铁
尽管奶牛贝茜发现每个平衡括号字符串都很美观,但她特别喜欢被她称为“完全”平衡的括号字符串----一个由 (
构成的字符串后接一个长度相同的 )
构成的字符串。
例如:
(((())))
有一天,当贝茜穿过牛棚时,她发现地面上有一个 N×N 的马蹄铁矩阵。每个马蹄铁的方向都看上去像 (
或 )
。
从矩阵的左上角开始,贝茜希望四处走动以拾起马蹄铁,使得她捡起的马蹄铁按顺序构成的括号字符串是完全平衡的。
请计算她能得到的最长完全平衡括号字符串的长度。
每一步中,贝茜可以沿上下左右四个方向移动。
她只能移动到包含马蹄铁的方格区域内,当她进入该区域时就会拿起那里的马蹄铁,并无法再次回到该位置(因为该位置没有马蹄铁了)。
她首先拿起的是左上角的马蹄铁。
由于她拿起的马蹄铁要形成一个完全平衡的字符串,因此她可能无法将所有马蹄铁都拿起来。
输入格式
第一行包含整数 N。
接下来 N 行,每行包含一个长度为 N 的括号字符串,用来表示括号矩阵。
输出格式
输出她能得到的最长完全平衡括号字符串的长度。
如果无法得到完全平衡括号字符串(例如,左上角马蹄铁形如 )
),则输出 0。
数据范围
2≤N≤5
输入样例:
4
(())
()((
(()(
))))
输出样例:
8
样例解释
贝茜的移动步骤如下:
1())
2)((
345(
876)
DFS深搜一下,左括号数量等于右括号数量时返回, 遇到")( "停止
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10;
int n, res;
char g[N][N];
bool st[N][N];
int dx[4]={-1, 0, 1, 0}, dy[4]={0, 1, 0, -1};
void dfs(int x, int y, int l, int r)
{
st[x][y] = true;
if(l == r){
st[x][y] = false;
res = max(res, l+r);
return ;
}
for(int i = 0; i < 4; i ++){
int nx = x+dx[i], ny = y+dy[i];
if(nx>=0 && nx<n && ny>=0 && ny<n && !st[nx][ny])
{
if(g[nx][ny]=='(' && g[x][y]==')') continue;
if(g[nx][ny]=='(') dfs(nx, ny, l+1, r);
else dfs(nx, ny, l, r+1);
}
}
st[x][y] = false;
}
int main()
{
cin >> n;
for(int i = 0; i < n; i++) cin >> g[i];
if(g[0][0]=='(') dfs(0, 0, 1, 0);
cout << res;
return 0;
}
1/9 (贪心+二分)AcWing 1996. 打乱字母
农夫约翰将按字典序排列的 N 头奶牛的名字列表贴在了牛棚的门上。
每个奶牛的名字都由一个长度介于 1 到 20 之间的由小写字母构成的唯一字符串表示。
麻烦制造者贝茜将列表中的奶牛名字重新排序打乱了列表。
此外,她还对每头奶牛的名字中的字母顺序进行了重新排列(也可能保持不变)。
给定修改过后的列表,请帮助约翰确定列表中的每个名字可能出现在原始列表中的最低和最高位置。
输入格式
第一行包含整数 N。
接下来 N 行,按照修改过后列表的顺序,给出了修改过后的奶牛的名字。
输出格式
共 N 行,第 i 行输出给定的第 i 个字符串在原始列表中可能的最低和最高位置。
数据范围
1≤N≤50000
输入样例:
4
essieb
a
xzy
elsie
输出样例:
2 3
1 1
4 4
2 3
样例解释
无论如何,字符串 “a” 必然出现在原始列表中第一个,类似的,字符串 “xzy” 必然出现在原始列表中的最后一个。
而字符串 “essieb” 和 “elsie” 都有可能位于原始列表的第 2 位或第 3 位,这取决于它们的原始字母顺序。
例如,”bessie” 和 “elsie” 分别位于 2,32,3 位,”sisbee” 和 “ilees” 分别位于 3,2 位。
贪心+二分
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 50010;
string s[N],a[N],b[N];//a:最大序列 b:最小序列
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> s[i];
a[i]=b[i]=s[i];
sort(a[i].begin(), a[i].end(), greater<char>());
sort(b[i].begin(), b[i].end());
}
sort(a+1, a+n+1);
sort(b+1, b+n+1);
for(int i = 1; i <= n; i ++)
{
string t = s[i];
sort(t.begin(), t.end());
int l = 1, r = n;
while(l != r)
{
int mid = (l+r)>>1;
if(t <= a[mid])
r = mid;
else l = mid + 1;
}
cout << l << " ";
reverse(t.begin(), t.end());
l = 1, r = n;
while(l != r)
{
int mid = (l+r+1)>>1;
if(t >= b[mid]) l = mid;
else r = mid - 1;
}
cout << l << endl;
}
return 0;
}
1/10 (差分+离散化)AcWing 1987. 粉刷栅栏
输入样例:
6
2 R
6 L
1 R
8 L
1 R
2 R
输出样例:
6
离散化+差分
#include <iostream>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std;
map<int, int> mp;
int main()
{
int t;
cin >> t;
int num, u = 0;
char op;
while(t --)
{
scanf("%d %c", &num, &op);
if(op=='R'){
mp[u+num]--;
mp[u]++;
u += num;
}else{
mp[u]--;
mp[u-num]++;
u -= num;
}
}
int res = 0, x = 0, pre;
for(auto& [m,v] : mp)
{
if(x > 1) res += m-pre;
x += v;
pre = m;
}
cout << res;
return 0;
}
1/11 (前/后序最值)AcWing 1978. 奶牛过马路
输入样例:
4
-3 4
7 8
10 16
3 9
输出样例:
2
样例解释
第一头牛和第三头牛的行动路线不与其他奶牛的路线相交。
第二头牛和第四头牛的行动路线相交。
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#define x first
#define y second
using namespace std;
const int N = 1e5+10;
typedef pair<int, int> PII;
vector<PII> v;
int lmax[N], rmin[N];
int main()
{
int n;
cin >> n;
int a, b;
for(int i = 0; i < n; i ++)
{
scanf("%d%d", &a, &b);
v.push_back({a, b});
}
sort(v.begin(), v.end());
int minNum=1e7, maxNum=-1e7;
lmax[1] = v[0].y;
rmin[n-2] = v[n-1].y;
for(int i = 2; i < n; i ++) lmax[i] = max(lmax[i-1], v[i-1].y);
for(int i = n-3; i >= 0; i --) rmin[i] = min(rmin[i+1], v[i+1].y);
int res = 0;
if(v[0].y < rmin[0]) res ++;
if(v[n-1].y > lmax[n-1]) res ++;
for(int i = 1; i < n-1; i ++)
{
if(v[i].y < rmin[i] && v[i].y > lmax[i]) res ++;
}
cout << res;
return 0;
}
1/12 AcWing 1969. 品种邻近
输入样例:
6 3
7
3
4
2
3
4
输出样例:
4
样例解释
一对品种 ID 为 3 的奶牛以及一对品种 ID 为 4 的奶牛属于拥挤奶牛对。
所以,最大拥挤奶牛对的品种 ID 为 4。
法一:
pre数组记录上一头 ID = i 的牛出现的位置。
判断位置之差是否小于等于 K,并更新答案
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6+10;
int pre[N];
int res = -1;
int main()
{
int n, k, x;
cin >> n >> k;
for(int i = 1; i <= n; i ++)
{
scanf("%d", &x);
if(pre[x] && i-pre[x]<=k) res = max(res, x);
pre[x] = i;
}
cout << res;
return 0;
}
法二:
滑动窗口
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 1e6+10;
int cnt[N];
int res = -1;
int main()
{
queue<int> q;
int n, k;
scanf("%d%d", &n, &k);
for(int i = 0; i < n; i ++)
{
int x;
scanf("%d", &x);
if(cnt[x] > 0) res = max(res, x);
q.push(x);
cnt[x] ++;
if(q.size()>k){
cnt[q.front()] --;
q.pop();
}
}
cout << res;
return 0;
}
1/13 AcWing 1960. 闪烁
1/14 (差分+离散化)AcWing 1952. 金发姑娘和 N 头牛
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
int main()
{
map<int, int> b;
int n, x, y, z;
scanf("%d%d%d%d", &n, &x, &y, &z);
for (int i = 0; i < n; i ++ )
{
int l, r;
scanf("%d%d", &l, &r);
b[-1] += x;
b[l] += y - x;
b[r + 1] -= y - z;
b[2e9] -= z;
}
int res = 0, sum = 0;
for (auto& [k, v]: b)
{
sum += v;
res = max(res, sum);
}
cout << res;
return 0;
}
1/15(水起来) AcWing 第 34 场周赛
- AcWing 4210. 数字
输入样例1:
5
输出样例1:
7/3
输入样例2:
3
输出样例2:
2/1
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int gcd(int a,int b){
if(b==0) return a;
return gcd(b,a%b);
}
int main()
{
int n;
cin >> n;
int res=0;
for(int i = 2; i < n; i ++)
{
int t = n;
while(t)
{
res += t%i;
t /= i;
}
}
int m=gcd(res,n-2);
cout << res/m << "/" << (n-2)/m;
return 0;
}
- (拓扑排序)AcWing 4211. 序列重排
输入样例1:
6
4 8 6 3 12 9
输出样例1:
9 3 6 12 4 8
输入样例2:
4
42 28 84 126
输出样例2:
126 42 84 28
输入样例3:
2
1000000000000000000 3000000000000000000
输出样例3:
3000000000000000000 1000000000000000000
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 110;
int n;
LL a[N],q[N];
int in[N];
bool g[N][N];
void topSort()
{
int front = -1, rear = -1;
for(int i = 0; i < n; i ++)
if(!in[i])
q[++rear] = i;
while(front != rear)
{
int t = q[++front];
for(int i = 0; i < n; i ++)
{
if(g[t][i])
{
in[i]--;
if(!in[i]){
q[++rear] = i;
}
}
}
}
for(int i = 0; i < n; i++) cout << a[q[i]] << " ";
}
int main()
{
cin >> n;
for(int i = 0; i < n; i ++) cin >> a[i];
sort(a, a+n);
for(int i = 0; i < n-1; i ++){
for(int j = i+1; j < n; j ++){
if(a[j]==a[i]*2)
g[i][j]=true, in[j]++;
else if(a[j]==a[i]*3)
g[j][i]=true, in[i]++;
}
}
topSort();
return 0;
}