A-咕咕东的奇遇
题目描述
咕咕东是个贪玩的孩子,有一天,他从上古遗迹中得到了一个神奇的圆环。这个圆环由字母表组成首尾相接的环,环上有一个指针,最初指向字母a。
咕咕东每次可以顺时针或者逆时针旋转一格。例如,a顺时针旋转到z,逆时针旋转到b。咕咕东手里有一个字符串,但是他太笨了,所以他来请求你的帮助,问最少需要转多少次。
输入格式
输入只有一行,是一个字符串。
输出格式
输出最少要转的次数。
样例输入
zeus
样例输出
18
思路
题目并不难,只需要每次找到最短路径然后更新位置即可。
找到最短路径的过程也很简单,因为一共26个字母,每次最短路径必定不超过13,如果超过13则用26减去该路径(即反方向转动)。
可以直接将字符转成int型,转出来的结果是字母的ASCII码。
PS
我写的时候思路有点清奇,我申请了一个int型的数组,一个一个输入的字符分别转化成int型。其实完全可以直接输入字符串然后再进行转化。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int c[10010];
int total = 0;
int main()
{
memset(c, 0, sizeof c);
char ch;
int i = 0;
while ((ch = cin.get()) != EOF)
{
if (ch == '\n') break;
c[i] = (int)ch;
i++;
}
int begin = 97;
i = 0;
while (c[i] != 0)
{
int b = c[i] - begin;
if (b < 0) b = -b;
if (b > 13) total += (26 - b);
else total += b;
begin = c[i];
i++;
}
std::cout << total << endl;
system("Pause");
}
B-咕咕东想吃饭
题目描述
咕咕东考试周开始了,考试周一共有n天。他不想考试周这么累,于是打算每天都吃顿好的。他决定每天都吃生煎,咕咕东每天需要买aia_iai个生煎。但是生煎店为了刺激消费,只有两种购买方式:
①在某一天一次性买两个生煎。
②今天买一个生煎,同时为明天买一个生煎,店家会给一个券,第二天用券来拿。
没有其余的购买方式,这两种购买方式可以用无数次,但是咕咕东是个节俭的好孩子,他训练结束就走了,不允许训练结束时手里有券。咕咕东非常有钱,你不需要担心咕咕东没钱,但是咕咕东太笨了,他想问你他能否在考试周每天都能恰好买ai个生煎。
输入格式
输入两行,第一行输入一个正整数n (1<=n<=100000),表示考试周的天数。
第二行有n个数,第i个数 ai(0<=ai<=10000,表示第i天咕咕东要买的生煎的数量。
输出格式
如果可以满足咕咕东奇怪的要求,输出"YES",如果不能满足,输出“NO”。
样例输入1
4
1 2 1 2
样例输出1
YES
样例输入2
3
1 0 1
样例输出2
NO
思路
这道题可以用递归进行求解,但我更喜欢不递归的写法。
首先分析这道题题目,不管是方案1还是方案2,因为有券必须用完的限制,所以若可以满足条件,生煎数目必须是偶数个。
其次,如果从前向后进行循环的话,并不能很好地选择方案,因为无法预知后面的天数会发生什么情况(虽然可以暴力求解,把每个情况尝试一遍)。因此循环是从最后一天开始的。
经过分析,选择奇数次方案2与选择一次结果是一样的,因为超过一次的奇数次,前后两天都可被方案1代替。
那么从后向前循环,改天生煎数为奇数,选择一次方案2并减少前面一天的一个生煎,否则选择方案1。直到第一天,如果剩余生煎数为奇数个或为负,则无法满足要求。
代码
#include <iostream>
using namespace std;
int total;
int sum;
int main()
{
bool ok = true;
cin>>total;
int *a = new int[total];
sum = 0;
for(int i = 0; i < total; i++)
{
cin>>a[i];
sum += a[i];
}
if(sum%2 == 1) ok = false;//总数是奇数
for(int i = total - 1; i >= 0 ;i--)
{
if(a[i]%2==1 && i==0)
{
ok = false;
break;
}
else if(a[i] < 0)
{
ok = false;
break;
}
else if(a[i]%2==1) a[i-1] -= 1;
}
if(ok) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
C - 可怕的宇宙射线
样例输入
4
4 2 2 3
样例输出
39
思路
我第一反应就是,既然是一层一层扩散的那就用bfs吧。因为申请了一个二维数组来记录该点有没有到达,涉及到坐标问题,所以并没有使用while(!q.empty( ) ) 这样的循环。
使用偏移量dx,dy来标记了每一个方向,这里是按照顺时针方向进行标记的(当然也可以逆时针方向标记,但是一定要标记成一个圈,不然后续选择方向就会很麻烦)。
声明一个point记录每个点的坐标与方向,每次将point加入队列。
过程
首先将起始点加入队列,根据方向进行扩散,沿途标记每一个经过的点,并将当前的终点加入队列。
剪枝
稍微思考一下,我们就可以知道,因为每次是向两个方向进行扩散的,所以这是一个指数级的问题,那么我们就要考虑剪枝。
剪枝方法是,将同一层的再一次到达同一个点并且同方向的射线剪枝。为什么需要同一层呢,因为如果不在同一层,下一次扩散的距离很可能不同,结果自然就会出错。因此声明了cut数组进行剪枝,每一次扩散之前先将cut数组清空。
PS
我考虑过对称性的问题,因为第一次一定是向上的,不难理解这个图是左右对称的。利用显然可以节省空间,但是会存在放射到中轴线由于数组空间不足而需要反射的情况,实现起来较为复杂。
代码
#include <iostream>
#include <cstring>
#include <queue>
#include <cstring>
using namespace std;
int dx[8] = { 0, -1, -1, -1, 0, 1, 1, 1 };
int dy[8] = { 1, 1, 0, -1, -1, -1, 0, 1 };
int total;
bool is[500][500];
bool cut[500][500][8];
struct point
{
pair<int, int> p;
int direct;
point(pair<int, int> &p1, int d)
{
p = p1;
direct = d;
}
};
int main()
{
total = 0;
memset(is, 0, sizeof is);
int n;//分裂次数
cin >> n;
int *a = new int[n];
for (int i = 0; i < n; i++)
cin >> a[i];
pair<int, int> san1(250, 250 + a[0] - 1);
for (int i = 0; i < a[0]; i++)
is[250][250 + i] = true;
struct point po(san1, 0);
queue<struct point> q;
q.push(po);
int go = 1;
for (int i = 1; i < n; i++)
{
//cout << "go" << go << endl;
int es = go;
go = 0;
memset(cut, 0, sizeof cut);
//cout << "es" << es << endl;
for (int j = 0; j < es; j++)
{
//cout<<i<<" "<<j<<endl;
struct point p1 = q.front();
int x = p1.p.first;
int y = p1.p.second;
int pot = p1.direct;
pair<int, int> pp3 = p1.p;
//cout << "pot:" << pot << endl;
if (pot == 0)
{
//cout<< cut[x][y][0] <<endl;
if (cut[x][y][0] == 0)
{
pair<int, int> pp3;
cut[x][y][0] = 1;
for (int k = 1; k <= a[i]; k++)
is[x + k * dx[1]][y + k * dy[1]] = true;
pp3.first = x + a[i] * dx[1];
pp3.second = y + a[i] * dy[1];
//cout << pp3.first << " " << pp3.second << endl;
struct point p3(pp3, 1);
q.push(p3);
//cout << "ye!!" << endl;
go++;
pair<int, int> pp4;
pp4.first = x + a[i] * dx[7];
pp4.second = y + a[i] * dy[7];
//cout << pp4.first << " " << pp4.second << endl;
for (int k = 1; k <= a[i]; k++)
is[x + k * dx[7]][y + k * dy[7]] = true;
struct point p4(pp4, 7);
q.push(p4);
go++;
}
q.pop();
}
else if (pot == 7)
{
if (cut[x][y][7] == 0)
{
pair<int, int> pp3;
cut[x][y][7] = 1;
for (int k = 1; k <= a[i]; k++)
is[x + k * dx[0]][y + k * dy[0]] = true;
pp3.first = x + a[i] * dx[0];
pp3.second = y + a[i] * dy[0];
//cout << pp3.first << " " << pp3.second << endl;
struct point p3(pp3, 0);
q.push(p3);
go++;
pair<int, int> pp4;
pp4.first = x + a[i] * dx[6];
pp4.second = y + a[i] * dy[6];
//cout << pp4.first << " " << pp4.second << endl;
for (int k = 1; k <= a[i]; k++)
is[x + k * dx[6]][y + k * dy[6]] = true;
struct point p4(pp4, 6);
q.push(p4);
go++;
}
q.pop();
}
else
{
if (cut[x][y][pot] == 0)
{
pair<int, int> pp3;
cut[x][y][pot] = 1;
for (int k = 1; k <= a[i]; k++)
is[x + k * dx[pot + 1]][y + k * dy[pot + 1]] = true;
pp3.first = x + a[i] * dx[pot + 1];
pp3.second = y + a[i] * dy[pot + 1];
//cout << pp3.first << " " << pp3.second << endl;
struct point p3(pp3, pot + 1);
q.push(p3);
go++;
pair<int, int> pp4;
pp4.first = x + a[i] * dx[pot - 1];
pp4.second = y + a[i] * dy[pot - 1];
//cout << pp4.first << " " << pp4.second << endl;
for (int k = 1; k <= a[i]; k++)
is[x + k * dx[pot - 1]][y + k * dy[pot - 1]] = true;
struct point p4(pp4, pot - 1);
q.push(p4);
go++;
}
q.pop();
}
}
}
for (int i = 0; i < 500; i++)
for (int j = 0; j < 500; j++)
if (is[i][j] == 1)
{
total++;
//cout << "(" << i << "," << j << ")";
}
cout << total << endl;
//std::system("pause");
}