目录
A. Linear Keyboard
给你一个包含26个键的键盘,每个键对应一个小写拉丁字母。你需要在键盘上打出一个单词,单词由小写拉丁字母组成。为了打印出该单词你需要移动到对应的按键上,花费的时间是相邻按键在键盘上的位置差(取绝对值)。
Input:
第一行的整数,表示测试用例的数量,随后两行是测试用例的描述;
第一行是长度为26的a~z组成的小写字符串keyboard。
第二行是需要打出的单词s。
Output:
输出在给出的键盘上打出单词s需要花费的最小时间。(实际上顺序打出单词没有什么最小时间)
题解:
思路比较简单,算出每个字母的位置存下来,然后遍历一遍累加差值即可。
代码1:
使用STL,string.find()可以查找字符在字符串中的位置。
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int main()
{
int cnt;
cin>>cnt;
while(cnt--){
string keyboard, answer;
cin>>keyboard>>answer;
vector<int> pos;
for(const auto& x: answer){
pos.push_back(keyboard.find(x));
}
int dis = 0;
for(size_t i = 1; i < pos.size() ; ++i){
dis += abs(pos[i]-pos[i-1]);
}
cout<<dis<<endl;
}
return 0;
}
代码2:
使用数组映射字符对应的位置
#include "bits/stdc++.h"
using namespace std;
int main(){
int32_t t;
cin >> t;
string s, s1;
int32_t a[26];
for (int32_t i = 0; i < t; i++) {
cin >> s;
for (int32_t j = 0; j < 26; j++) a[s[j] - 'a'] = j;
cin >> s1;
int32_t ans = 0;
for (int32_t j = 1; j < s1.size(); j++) ans+= abs(a[s1[j - 1] -'a'] - a[s1[j] - 'a']);
cout << ans << endl;
}
B. Odd Grasshopper
有一个蝗虫在x坐标轴上的x0位置上。它可以向左跳d步到x0-d,也可以向右跳到x0+d的位置上。跳跃的距离从1开始递增,跳跃的方向是由它所在的位置决定,如果坐标是偶数就向左跳,奇数向右跳。
Input
输入起始坐标x0,跳跃次数n。
Output
跳跃n次后,蝗虫的坐标
题解:
手动模拟了一下从奇数和偶数的坐标出发,跳跃4次后会回到原点。n如果是4的倍数,最后还是回到原点。如果不是,模拟蝗虫从x0出发,跳跃n%4次的结果。
代码1:
#include <iostream>
using namespace std;
using ll = long long;
ll jpm(ll x0, ll n, ll i){
while(n--){
if(x0%2 == 0){
x0-=i;
} else{
x0+=i;
}
++i;
}
return x0;
}
int main()
{
int cnt;
cin>>cnt;
while(cnt--){
ll x0,n;
cin>>x0>>n;
if(n%4 == 0){
cout<<x0<<endl;
} else{
cout<<jpm(x0,n%4,(n/4)*4+1)<<endl;
}
}
return 0;
}
代码2:
大神(博客主人)的超精简版代码,思路是一样的。
#include "bits/stdc++.h"
using namespace std;
int main(){
int32_t t;
cin >> t;
while (t--) {
int64_t a, b, c;
cin >> a >> b;
c = (b - b % 4);
while (++c <= b) {
if (a % 2 == 0) a -= c;
else a+=c;
}
cout << a << endl;
}
}
C. Minimum Extraction
你有包含n个整数的数组a,如果数组长度大于1,你要找到这个数组最小的数m,如果有多个可以选任一个,然后将m从这个数组中移除,剩下的每个元素减去m,这个操作叫minimum extraction。
你想在这个操作过程中使数组的最小值尽可能大,为了达到这个目的,你可以进行任意次minimum extraction操作。
Input
数组长度和数组a
Output
minimum extraction后数组最大的最小值
题解:
先将代码排序,每次minimum extraction后数组的最小值就是a[i+1]-a[i],遍历一下找出a[i+1]-a[i]的最大值即可
代码1:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int cnt;
cin>>cnt;
while(cnt--){
int n;
cin>>n;
vector<int> nums(n,0);
for(int i = 0 ; i < n ; ++i){
cin>>nums[i];
}
sort(nums.begin(),nums.end());
int maxn = nums[0];
for(int i = 0 ; i < n-1 ; ++i){
maxn = max(maxn, nums[i+1]-nums[i]);
}
cout<<maxn<<endl;
}
return 0;
}
D. Blue-Red Permutation
有长度为n的数组a,数组的元素可以相同,每个元素都被标记为red或blue,被标记为blue的元素每次只能减1,被标记为red的元素每次只能加1。有没有可能多次操作后,使数组成为1~n的序列。
Input:
数组长度n,数组a,由'R'和'B’组成的字符串,标记元素的颜色。
Output:
YES 或者NO,表示是否可以转换成1~n的序列
4
1 2 5 2
BRBR
题解:
先把数组重新排序,标记为B的放前面,R放后面,然后按照数字大小排序,排序后是这样
1 5 2 2
B B R R
第i个位置上的元素值为ai,如果被标记为B,如果ai小于i,因为只能递减就不可能转化为i,,R类似,如果ai大于i,只能递增也不可能转化为i。
代码1:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
int cnt;
cin>>cnt;
while(cnt--){
int n;
cin>>n;
vector<int> num(n,0);
string color;
for(int i = 0 ; i < n ; ++i){
cin>>num[i];
}
cin>>color;
vector<int> blue;
vector<int> red;
for(int i = 0 ; i < n ; ++i){
if(color[i] == 'B'){
blue.push_back(num[i]);
} else {
red.push_back(num[i]);
}
}
sort(blue.begin(),blue.end());
sort(red.begin(),red.end());
bool b = true;
for(int i = 0 ; i < blue.size() ; ++i){
if(blue[i]<=i){
b = false;
break;
}
}
bool r = true;
for(int i = 0,j = n-red.size()+1 ; i < red.size() ; ++i,++j){
if(red[i] > j){
r = false;
break;
}
}
if(b && r){
cout<<"Yes"<<endl;
} else{
cout<<"No"<<endl;
}
}
return 0;
}
大神代码:
#include "bits/stdc++.h"
using namespace std;
int32_t a[200010];
string s;
int main(){
int32_t t, n;
scanf("%d", &t);
while (t--) {
vector<int32_t> l, r;
scanf("%d", &n);
for (int32_t i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
cin >> s;
for (int32_t i = 0; i < n; i++) {
if (s[i] == 'B') l.push_back(a[i]);
else r.push_back(a[i]);
}
sort(l.begin(), l.end());
sort(r.begin(), r.end(), greater<int32_t>());
bool flag = true;
for (int i = 0; i < l.size(); i++)
if (l[i] < i + 1) flag = false;
for (int i = 0; i < r.size(); i++) {
if (r[i] > n - i) flag = false;
}
if (!flag) {
printf("NO\n");
} else {
printf("YES\n");
}
}
}
E. Robot on the Board 1
有一个机器人在一个n x m的棋盘上,行数从上到下从1到n进行编号,列数从左到右从1到n进行编号。这个机器人可以从一个格子移动到另一个相邻的格子。我们会给机器人一串命令由 'L', 'R', 'D','U'组成,代表着可以向左、右、下、上移动。
机器人可以从任一个格子出发,开始按顺序执行命令,如果机器人移动超过棋盘的边界,就结束,如果一个命令导致机器人出界,就认为执行失败。
为了使机器人可以执行尽可能多的命令不掉下去,我们要选取一个起始位置。
Input
输入n,m表示棋盘的高度和宽度,然后是由'L', 'R', 'D', 'U组成的字符串表示将要执行的命令。
Output
输出能成功执行最多命令的起始位置,如果有多个可以选取任意一个。
题解:
假设从原点出发,模拟执行命令,记录每次命令执行后的x,y坐标,并记录最左、最右、最上和最下的点,如果最左到最右、最上到最下的距离超出了范围就失败,否则就调整起点位置。
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int cnt;
cin>>cnt;
while(cnt--){
int m,n;
cin>>m>>n;
string command;
cin>>command;
int x0,y0;
x0 = y0 = 1;
int right,left,up,down;
right = left = up = down = 0;
int x,y;
x = y = 0;
for(const auto & c:command){
switch(c){
case 'R':
y++;break;
case 'L':
y--;break;
case 'U':
x--;break;
case 'D':
x++;break;
}
right = max(right,x);
left = min(left,x);
down= max(down,y);
up = min(up,y);
if(abs(right-left) >= m || abs(up-down) >= n){
break;
}
x0 = 1 - left;
y0 = 1 - up;
}
cout<<x0<<" "<<y0<<endl;
}
return 0;
}
F. Robot on the Board 2
题意和E题差别在于给出机器人在每个格子上都一个固定的行走方向,增加一个条件如果机器人走到之前访问过的位置也失败,
输入和E题相同
输出可以能成功执行最多命令的起始位置和移动的最大的步数
题解:
感谢cxj提供本题思路。一个简单暴力的做法,只需要从每一个节点出发,dfs到不能走的地方为止,记录能走多远。时间复杂度上限是棋盘大小的平方。
以这个思路作为基础,两个优化点:
1、从一个节点出发到停止为止的路径上的每一个点,作为初始节点出发到达的终点都是在相同的边界,或者在一个环上。都可以一次性算出这些点到达终点能走的步数,要么是到达边界的总步数,要么是环的大小。
2、从一个点到达一个前面已经计算过的点时,那么就进入了之前遍历过的路径,不需要重复更新。
注意的坑点:因为需要处理环,所以用一个数组记录到达过的点。
因为可能会出现栈溢出,所以我必须用循环代替递归改写了dfs才通过此题。。。但是cxj却递归过了,不知道这是什么魔法。
具体实现如下
非递归代码(xt):
#include "bits/stdc++.h"
using namespace std;
char c[2010][2010];
int32_t h[2010][2010];
int32_t t, n, m;
int32_t flagx, flagy, sizeC;
int main(){
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
for (int32_t i = 0; i < n; i++) {
scanf("%s", c[i]);
}
for (int32_t i = 0; i < n; i++) {
for (int32_t j = 0; j < m; j++) {
h[i][j] = -1;
}
}
for (int32_t i = 0; i < n; i++) {
for (int32_t j = 0; j < m; j++) {
if (h[i][j] >= 0) continue;
flagx = flagy = -1;
set<pair<int, int> > s;
vector<pair<int, int> >st;
int x = i;
int y = j;
while (true) {
st.push_back(make_pair(x, y));
s.insert(make_pair(x, y));
int32_t xx = x;
int32_t yy = y;
//printf("%d %d\n", x, y);
if (c[x][y] == 'D') xx++;
if (c[x][y] == 'U') xx--;
if (c[x][y] == 'R') yy++;
if (c[x][y] == 'L') yy--;
if (xx < 0 || xx >= n || yy < 0 || yy >= m) {
h[x][y] = 0;
break;
}
if (h[xx][yy] != -1) {
h[x][y] = h[xx][yy] + 1;
break;
}
if (s.find(make_pair(xx, yy)) != s.end()) {
h[x][y] = 0;
flagx = xx;
flagy = yy;
break;
}
x = xx;
y = yy;
}
int pflag = -1;
for (int32_t i = st.size() - 2; i >= 0; i--) {
h[st[i].first][st[i].second] = h[st[i + 1].first][st[i + 1].second] + 1;
if (flagx == st[i].first && flagy == st[i].second) {
pflag = i;
}
}
for (int32_t i = pflag; i < st.size(); i++) {
h[st[i].first][st[i].second] = h[flagx][flagy];
}
}
}
int32_t ansx = 0;
int32_t ansy= 0;
for (int32_t i = 0; i < n; i++) {
for (int32_t j = 0; j < m; j++) {
if (h[ansx][ansy] < h[i][j]) {
ansx = i;
ansy = j;
}
}
}
printf("%d %d %d\n", ansx + 1, ansy + 1, h[ansx][ansy] + 1);
}
}
递归代码(cxj):
#include <bits/stdc++.h>
#define x first
#define y second
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N=2005;
int n,m;
int dist[N][N];
char g[N][N];
bool st[N][N];
PII p[N*N];
int cnt;
int dfs(int x,int y){
p[++cnt]={x,y};
if(x<1||x>n||y<1||y>m||st[x][y]) return dist[x][y];
st[x][y]=true;
if(g[x][y]=='U') dist[x][y]=dfs(x-1,y)+1;
if(g[x][y]=='D') dist[x][y]=dfs(x+1,y)+1;
if(g[x][y]=='L') dist[x][y]=dfs(x,y-1)+1;
if(g[x][y]=='R') dist[x][y]=dfs(x,y+1)+1;
return dist[x][y];
}
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>g[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(!st[i][j]){
cnt=0;
dfs(i,j);
//如果有环,要更新整个环的dist
PII t=p[cnt];
for(int k=1;k<cnt;k++){
if(p[k]==t){
for(int h=k;h<=cnt;h++)
dist[p[h].x][p[h].y]=cnt-k;//这里的长度应该是整个环的长度
}
}
}
}
int x=1,y=1,res=dist[x][y];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(dist[i][j]>res){
x=i,y=j,res=dist[i][j];
}
}
cout<<x<<" "<<y<<" "<<res<<'\n';
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
st[i][j]=false,dist[i][j]=0;
}
int main(){
ios_base::sync_with_stdio(false);
int T;
cin>>T;
while(T--)solve();
}
G. Banquet Preparations 1
你有两个整数数组a1...an与b1...bn,你需要对于每一个1<=i<=n, 从ai与bi中一共减掉总和为m的整数,使得尽量小
输入:
t组数据,每组包含一个n和m,以及n行的ai 与 bi。
输出:
t组数据,每组包含最小的所求的值,然后n行,每行包含ai和bi的减小大小。
题解:
感谢cxj提供本题思路。
对于一组ai与bi,假设ai减小a,那么bi减小m-a,那么变化后的就等于,其中ai - bi + m是定值,只需要关注2a的范围即可。
那么同理,如果所有的ai一共扣去了A,那么所有的bi一共扣去了nm - A,那么我们所要求的最小值为:,式子中只有A为变量,其余都是定值,因此只需要得到A的取值范围区间,根据该表达式的分段函数,判断其值。
我们可以确定每一组ai、bi可以提供的最小ai的减少量以及最大减少量,通过累加得到A的最小值与最大值。
代码实现如下:
#include "bits/stdc++.h"
using namespace std;
const int32_t MAXN = 200000 + 10;
long long totala, totalb;
long long a[MAXN], b[MAXN], ansA[MAXN], ansB[MAXN];
int main(){
long long n, m, t;
scanf("%lld", &t);
while (t--) {
scanf("%lld%lld", &n, &m);
totala = totalb = 0;
long long mina = 0, minb = 0;
for (int32_t i = 0; i < n; i++) {
scanf("%lld%lld", &a[i], &b[i]);
ansA[i] = a[i];
ansB[i] = b[i];
totala += a[i];
totalb += b[i];
if (b[i] < m) {
mina += (m - b[i]);
}
if (a[i] < m) {
minb += (m - a[i]);
}
}
long long constT = totala - totalb + 1LL * n * m;
long long optA = 0;
if (2 * mina > constT) optA = mina;
if (2 * mina <= constT && constT <= 2 * (n * m - minb)) optA = constT / 2;
if (constT > 2 * (n * m - minb)) optA = n * m - minb;
for (int32_t i = 0; i < n; i++) {
if (ansB[i] >= m) {
ansB[i] -= m;
} else {
ansB[i] = 0;
ansA[i] -= (m - b[i]);
}
}
printf("%lld\n", abs(constT - 2 * optA));
optA -= mina;
for (int32_t i = 0; i < n; i++) {
long long tmp = min(ansA[i], b[i] - ansB[i]);
if (tmp < optA) {
optA -= tmp;
ansA[i] = ansA[i] - tmp;
ansB[i] = ansB[i] + tmp;
} else {
ansA[i] -= optA;
ansB[i] += optA;
optA = 0;
}
printf("%lld %lld\n", a[i] - ansA[i], b[i] - ansB[i]);
}
}
}
H. Banquet Preparations 2
题意与G不同之处,每一组ai,bi的m值都可能不同,为mi。且最终需要让最终不同的(ai, bi)对数尽量少。
题解:
因为ai + bi - mi不同的组,一定得到的(ai, bi)不同,我们只需要对ai + bi - mi相同的作为一个集合,集合里得到的(ai, bi)尽量少。
那么我们可以通过ai, bi, mi得到每一组的(ai,bi)的可能取值范围,只要确定了ai的最大值最小值即可确定bi范围。这个的处理与上一题做法是相同的
(ai, bi, mi)与(aj, bj, mi)可以减完后变成同一组,当且仅当ai的取值范围与aj的取值范围有交点或重叠。
我们对于每组的ai取值范围作为区间,排序,贪心的取尽量靠后的点作为ai的取值,用尽可能少的点,使得所有的区间都被ai覆盖。即为最终不同的(ai,bi)组数。
代码:
#include "bits/stdc++.h"
using namespace std;
const int32_t MAXN = 200000 + 10;
struct food{
int32_t total;
int32_t l, r, a, b, m;
int32_t no;
} f[MAXN];
bool cmp(food a, food b)
{
if (a.total != b.total) {
return a.total < b.total;
} else {
return a.l < b.l;
}
}
int32_t countA(food f[], int32_t n)
{
int32_t j = 0;
int32_t minr = 0;
int32_t ans = 0;
for (int32_t i = 0; i < n; i = j) {
minr = f[i].r;
j = i + 1;
while (j < n && f[j].l <= minr) {
minr = min(f[j].r, minr);
j++;
}
for (int k = i; k < j; k++) {
f[k].l = minr;
}
ans++;
}
return ans;
}
int32_t cmp1(food a, food b)
{
return a.no < b.no;
}
int main(){
int32_t n, t;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int32_t i = 0; i < n; i++) {
scanf("%d%d%d", &f[i].a, &f[i].b, &f[i].m);
f[i].total = f[i].a + f[i].b - f[i].m;
if (f[i].a > f[i].m) {
f[i].l = f[i].a - f[i].m;
} else {
f[i].l = 0;
}
if (f[i].b > f[i].m) {
f[i].r = f[i].a;
} else {
f[i].r = f[i].a - (f[i].m - f[i].b);
}
f[i].no = i;
}
sort(f, f + n, cmp);
int32_t r = 0;
int32_t ans = 0;
for (int32_t l = 0; l < n; l = r) {
r = l + 1;
while (r < n && (r == 0 || f[r].total == f[r - 1].total)) {
r++;
continue;
}
ans += countA(f + l, r - l);
}
printf("%d\n", ans);
sort(f, f + n, cmp1);
for (int32_t i = 0; i < n; i++) {
printf("%d %d\n", f[i].a - f[i].l, f[i].b - (f[i].a + f[i].b - f[i].m - f[i].l));
}
}
}