蓝桥杯15届B组题解(二)
数字接龙
题目描述
小蓝最近迷上了一款名为《数字接龙》的迷宫游戏,游戏在一个大小为N × N 的格子棋盘上展开,其中每一个格子处都有着一个 0 . . . K − 1 之间的整数。游戏规则如下:
\1. 从左上角 (0, 0) 处出发,目标是到达右下角 (N − 1, N − 1) 处的格子,每一步可以选择沿着水平/垂直/对角线方向移动到下一个格子。
\2. 对于路径经过的棋盘格子,按照经过的格子顺序,上面的数字组成的序列要满足:0, 1, 2, . . . , K − 1, 0, 1, 2, . . . , K − 1, 0, 1, 2 . . . 。
\3. 途中需要对棋盘上的每个格子恰好都经过一次(仅一次)。
\4. 路径中不可以出现交叉的线路。例如之前有从 (0, 0) 移动到 (1, 1),那么再从 (1, 0) 移动到 (0, 1) 线路就会交叉。
为了方便表示,我们对可以行进的所有八个方向进行了数字编号,如下图2 所示;因此行进路径可以用一个包含 0 . . . 7 之间的数字字符串表示,如下图 1是一个迷宫示例,它所对应的答案就是:41255214。
现在请你帮小蓝规划出一条行进路径并将其输出。如果有多条路径,输出字典序最小的那一个;如果不存在任何一条路径,则输出 −1。
输入格式
第一行包含两个整数 N、K。接下来输入 N 行,每行 N 个整数表示棋盘格子上的数字。
输出格式
输出一行表示答案。如果存在答案输出路径,否则输出 −1。
样例输入
3 3
0 2 0
1 1 1
2 0 2
样例输出
41255214
提示
【样例说明】行进路径如图 1 所示。
【评测用例规模与约定】对于 80% 的评测用例:1 ≤ N ≤ 5。对于 100% 的评测用例:1 ≤ N ≤ 10,1 ≤ K ≤ 10。
解题思路
难点在于,如何处理交叉问题。
用深度搜索的话
假如某个点要到【-1,-1】左上的点,只需要检查【-1,0】【0,-1】是否走过即可。其他的右上,右下,左下,以此类推。
当走时,优先选择字典序小的方向,及从0,1,2,3一直到7。深度搜索时,从字典序小的方向依次探测到字典序大的方向。
#include<bits/stdc++.h>
using namespace std;
const int N =10+2;
int g[N][N];
int vis[N][N];
int n;
int k;
int dx[]={-1,-1,0,1,1,1,0,-1};
int dy[]={0,1,1,1,0,-1,-1,-1};
int tt;
bool finds=false;
void dfs(int x,int y,vector<char>& s,int pt)
{
if(finds)return;
vis[x][y]=1;
if(x==n&&y==n)
{
//第一个找到满足条件的路径,就是所求路径。
if(s.size()==n*n-1)
{
for(auto c:s)
cout<<c;
finds =true;
}
vis[x][y]=0;
return;
}
if(x<1||y<1||x>n||y>n)
return;
for(register int i=0;i<8;i++)
{
int ct = abs(dx[i])+abs(dy[i]);
int tx = dx[i]+x;
int ty = dy[i]+y;
if(g[tx][ty]!=(pt+1)%k)
{
continue;
}
if(ct>1&&vis[tx][y]&&vis[x][ty]||vis[tx][ty])
continue;
char c = ('0'+i);
s.push_back(c);
dfs(tx,ty,s,(pt+1)%k);
s.pop_back();
}
vis[x][y]=0;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
//判断特殊情况
if(n==1)
{
cout<<"-1";
return 0;
}
for(register int i=1;i<=n;i++)for(register int j=1;j<=n;j++)cin>>g[i][j];
vector<char >c;
dfs(1,1,c,0);
if(!finds) cout<<"-1";
}
爬山
题目描述
小明这天在参加公司团建,团建项目是爬山。在 x 轴上从左到右一共有 n座山,第 i 座山的高度为 hi。他们需要从左到右依次爬过所有的山,需要花费的体力值为 S = Σni=1hi。
然而小明偷偷学了魔法,可以降低一些山的高度。他掌握两种魔法,第一种魔法可以将高度为 H 的山的高度变为 ⌊√H⌋,可以使用 P 次;第二种魔法可以将高度为 H 的山的高度变为 ⌊H/2⌋,可以使用 Q 次。并且对于每座山可以按任意顺序多次释放这两种魔法。
小明想合理规划在哪些山使用魔法,使得爬山花费的体力值最少。请问最优情况下需要花费的体力值是多少?
输入格式
输入共两行。
第一行为三个整数 n,P,Q。
第二行为 n 个整数 h1,h2,. . . ,hn。
输出格式
输出共一行,一个整数代表答案。
样例输入
4 1 1
4 5 6 49
样例输出
18
提示
【样例说明】将第四座山变为 ⌊√49⌋ = 7,然后再将第四座山变为 ⌊7/2⌋ = 3。体力值为 4 + 5 + 6 + 3 = 18。
【评测用例规模与约定】
对于 20% 的评测用例,保证 n ≤ 8,P = 0。
对于 100% 的评测用例,保证 n ≤ 100000,0 ≤ P ≤ n,0 ≤ Q ≤ n,0 ≤ hi ≤ 100000。
解题思路
为了保证更好的节省体力,优先对大的数进行调整。
⌊
1
1
/
2
⌋
=
1
,
⌊
1
/
2
⌋
=
0
⌊
2
1
/
2
⌋
=
1
,
⌊
2
/
2
⌋
=
1
⌊
3
1
/
2
⌋
=
1
,
⌊
3
/
2
⌋
=
1
⌊
4
1
/
2
⌋
=
2
,
⌊
4
/
2
⌋
=
2
⌊
5
1
/
2
⌋
=
2
,
⌊
5
/
2
⌋
=
2
.
.
.
.
⌊
9
1
/
2
⌋
=
3
,
⌊
9
/
2
⌋
=
4
\lfloor 1^{1/2} \rfloor=1,\lfloor 1/2 \rfloor =0\\ \lfloor 2^{1/2} \rfloor=1,\lfloor 2/2 \rfloor =1\\ \lfloor 3^{1/2} \rfloor=1,\lfloor 3/2 \rfloor =1\\ \lfloor 4^{1/2} \rfloor=2,\lfloor 4/2 \rfloor =2\\ \lfloor 5^{1/2} \rfloor=2,\lfloor 5/2 \rfloor =2\\ ....\\ \lfloor 9^{1/2} \rfloor=3,\lfloor 9/2 \rfloor =4\\
⌊11/2⌋=1,⌊1/2⌋=0⌊21/2⌋=1,⌊2/2⌋=1⌊31/2⌋=1,⌊3/2⌋=1⌊41/2⌋=2,⌊4/2⌋=2⌊51/2⌋=2,⌊5/2⌋=2....⌊91/2⌋=3,⌊9/2⌋=4
根据推理,需要将最大的数,进行这两种操作后选最小的
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, P, Q;
long long ans = 0;
priority_queue<int> h;
cin >> n >> P >> Q;
for (int i = 0; i < n; i++)
{
int hi;
cin >> hi;
h.push(hi);
}
while (P || Q)
{
int first = h.top();
h.pop();
if (P && Q)
{
//如果要单纯的把测试用例全部过了
// if(P==1&Q==1&&h.size()==1)
// {
// int t2 = h.top();
// h.pop();
// int res1 = (int)sqrt(first)+t2/2;
// int res2 = (int)sqrt(t2)+first/2;
// h.push(min(res1,res2));
// Q--,P--;
// break;
// }
//
if (sqrt(first) <= first / 2)
{
h.push(sqrt(first));
P--;
}
else
{
h.push(first / 2);
Q--;
}
}
//如果只剩一种魔法了,就直接用
else if (P)
{
h.push(sqrt(first));
P--;
}
else if (Q)
{
h.push(first / 2);
Q--;
}
}
while (!h.empty())
{
ans += h.top();
h.pop();
}
cout << ans;
return 0;
}
不过本题分拿不满, 因为有特例情况。例如其中之一
2 1 1
48 49
正确答案是30,实则按代码跑出来31
{
h.push(first / 2);
Q–;
}
}
while (!h.empty())
{
ans += h.top();
h.pop();
}
cout << ans;
return 0;
}