[XUPT_ACM]寒假第一次比赛题解
写在前面:本次比赛共11题,设计的是A-E是简单题,F-H为中等题,I-K为难题,考虑到比赛的友好性,我们并没有将题目难度打乱,而是基本上按照从简单到难来排序。比赛的总体出题和预期的差不多,其中B题出题数较少而实际上B题是比较简单的一道题(可能是题目没看懂吧),而J题的过题数比预计的要多很多(看来大家数学都不错)。
比赛地址:https://vjudge.net/contest/280657
下面就对比赛的题目做一下简单的解答(题目方法不唯一,给出的解法仅供参考):
神秘连接
A题 B题 C题 D题 E题 F题 G题 H题 I题 J题 K题
A题
题意:
魔法球可以用魔法晶体来合成:
黄=2*黄(晶体)
绿=1*黄(晶体)+1*蓝(晶体)
蓝=3*蓝(晶体)
已知有A个黄色晶体B个蓝色晶体,要得到x黄y绿z蓝个魔法球,问最少还差几个晶体。
分析:
算出x黄y绿z蓝个魔法球所需的黄色晶体AS和蓝色晶体总数BS,如果不够就在结果中加上(AS-A)和(BS-B)。
注意要用long long!
参考代码:
#include <cstdio>
#include <iostream>
// 注意要用long long
typedef long long ll;
using namespace std;
ll a,b,as=0,bs=0;
ll x,y,z,ans=0;
int main()
{
cin >> a >> b;
cin >> x >> y >> z;
// 计算所需的黄色晶体和蓝色晶体数量
as=2*x+y;
bs=3*z+y;
// 如果不够就更新答案
if (as>a) ans+=(as-a);
if (bs>b) ans+=(bs-b);
cout << ans << endl;
return 0;
}
B题
题意:
这个题意可能比较难理解,其实就是有从左到右n个塔,然后相邻两个塔之间a-b层是相通的。问从 ta塔fa层 到 tb塔fb层 的最少步数,移动一次算一步。
分析:
这里我分了以下几种情况:
1.在同一个塔里的:abs(fa-fb)
2.不在一个塔里的:
(fa<a):先走到a层,再走到tb塔,再走到fb层:abs(fa-a)+abs(ta-tb)+abs(fb-a)
(fa>b):先走到b层,再走到tb塔,再走到fb层:abs(fa-b)+abs(ta-tb)+abs(fb-b)
其他 :直接走到tb塔,再走到fb层:abs(ta-tb)+abs(fa-fb)
参考代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
int n,h,a,b,k,ans;
int ta,fa,tb,fb;
int main()
{
cin >> n >> h >> a >> b >> k;
while(k--)
{
ans=0;
cin >> ta >> fa >> tb >> fb;
if (ta==tb)
// 直接走到fb层
ans=abs(fa-fb);
else
if (fa<a)
// 先走到a层,再走到tb塔,再走到fb层
ans=abs(fa-a)+abs(ta-tb)+abs(fb-a);
else if (fa>b)
// 先走到b层,再走到tb塔,再走到fb层
ans=abs(fa-b)+abs(ta-tb)+abs(fb-b);
else
// 直接走到tb塔,再走到fb层
ans=abs(ta-tb)+abs(fa-fb);
cout << ans << endl;
}
return 0;
}
C题
题意:
键盘被偷了,键盘编号是x,x+1....x+n-1。已知还剩下的键盘编号,问最少丢失的键盘数目。
分析:
直接令剩下键盘编号中最小的为x最大的为x+n-1,于是总键盘数就为最大-最小+1,丢失的键盘数目就是 最大-最小+1-n 。
参考代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int n;
int a[1005];
int main()
{
cin >> n;
for (int i=1;i<=n;i++)
cin >> a[i];
// 排序
sort(a+1,a+n+1);
cout << a[n]-a[1]+1-n << endl;
return 0;
}
D题
题意:
玩游戏拿数字,先手要使最后剩下数字最小,后手相反,拿到只剩下一个为止。
分析:
先手肯定拿最大,后手肯定拿最小。当然纯暴力就可以了,不过有一个稍微优雅一点的解法。我们先排序,然后排完序之后的最中间那个数字就是剩下的数字(偶数的话是中间偏左),直接输出这个数字就是最终结果。
参考代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int n;
int a[1005];
int main()
{
cin >> n;
for (int i=1;i<=n;i++)
cin >> a[i];
// 排序
sort(a+1,a+n+1);
// a[(n+1)/2]就是中间的数
cout << a[(n+1)/2] << endl;
return 0;
}
E题
题意:
用1*2的地砖铺地板,只能横着铺,可以碎成两个1*1。中间有一个矩形不用铺,给出位置。
分析:由于只能横着铺,就不用考虑怎么铺了,只要考虑需要几个1*1的即可。
我们把拖拉机(中间矩形)的上下和左右分开来考虑。
对于拖拉机上下:每一行需要的1*1就是m%2,而上有(x1-1)行,下有(n-x2)行,总共有(x1-1+n-x2)行
对于左右:每一行需要的1*1,左边需要(y1-1)%2,右边需要(m-y2)%2,而总共有(x2-x1+1)行
综上所述:sum(1*1的个数)=(x1-1+n-x2)*(m%2)+(x2-x1+1)*((y1-1)%2+(m-y2)%2)
最终的答案:(sum+1)/2;
参考代码:
#include <cstdio>
#include <iostream>
using namespace std;
int n,m,x1,x2,y1,y2;
int sum=0;
int main()
{
cin >> n >> m >> x1 >> y1 >> x2 >> y2;
// 1*1的总数
sum=(x1-1+n-x2)*(m%2)+(x2-x1+1)*((y1-1)%2+(m-y2)%2);
cout << (sum+1)/2 << endl;
return 0;
}
F题
题意:
你有一种特殊的笔,你需要伪造签名,问你能不能伪造?
分析:
暴力枚举每个位置,用vis来标记。每次遇到'#'我们都有两种处理方法。
之前没标记过的(第一次遇到):这个点必为特殊笔的左上角,涂色,如果发现涂色的地方有一处不是'#',则不可能伪造。
之前标记过的(不是第一次遇到):可以这个点为左上角涂色(也可以不涂),如果可以涂则涂色(这是贪心的思想)。
枚举完还可以伪造则说明可以 伪造。
参考代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m;
char Map[1005][1005];
int vis[1005][1005];
// 以某个位置为左上角涂色
int dir[9][2]={ {0,0},{0,1},{0,2},{1,0},{1,2},{2,0},{2,1},{2,2} };
int main()
{
memset(vis,0,sizeof(vis));
// 读入
cin >> n >> m; getchar();
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
scanf("%c",&Map[i][j]);
getchar();
}
// 遍历找'#'
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
// 为'#'但未标记
if (Map[i][j]=='#' && !vis[i][j])
{
// 越界,不可能涂色,不能伪造
if (i>n-2 || j>m-2)
{
printf("NO");
return 0;
}
// 以(i,j)为左上角涂色,若不可以则说明不能伪造
for (int k=0;k<9;k++)
{
int nx=i+dir[k][0],ny=j+dir[k][1];
vis[nx][ny]=1;
if (Map[nx][ny]!='#')
{
printf("NO");
return 0;
}
}
}
// 为'#'已标记
else if (Map[i][j]=='#')
{
// 判断能否涂色
int flag=1;
for (int k=0;k<9;k++)
{
int nx=i+dir[k][0],ny=j+dir[k][1];
if (Map[nx][ny]!='#')
{
flag=0;
break;
}
}
// 若能涂色就涂色
if (flag)
for (int k=0;k<9;k++)
{
int nx=i+dir[k][0],ny=j+dir[k][1];
vis[nx][ny]=1;
}
}
}
// 遍历结束,可以涂色
printf("YES\n");
return 0;
}
G题
题意:
问你扫雷怎么样才能赢。
分析:
其实就是看这个棋盘符不符合规则。我们可以碰到一个雷就把周围一圈的数字都减一,如果最后整个棋盘只有雷和0(包括.),就说明符合规则。
参考代码:
#include <cstdio>
#include <iostream>
using namespace std;
int n,m;
// 周围一圈
int dir[8][2]={ {-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1} };
char Map[105][105];
bool flag=true;
// 判断是否在棋盘内
bool judge(int x,int y)
{
if (x>=0 && x<n && y>=0 && y<m)
return true;
else
return false;
}
int main()
{
// 读入
scanf("%d%d",&n,&m); getchar();
for (int i=0;i<n;i++)
scanf("%s",Map[i]);
for (int i=0;i<n;i++)
for (int j=0;j<m;j++)
if (Map[i][j]=='*')
{
// 把周围一圈都减一
for (int k=0;k<8;k++)
{
int nx=i+dir[k][0],ny=j+dir[k][1];
// 周围一圈不是雷
if (judge(nx,ny) && Map[nx][ny]!='*')
{
// 如果减完小于0了,那就肯定不能赢了,直接令flag=false
if (Map[nx][ny]!='0' && Map[nx][ny]!='.')
Map[nx][ny]--;
else
flag=false;
}
}
}
// 判断能不能赢
for (int i=0;i<n;i++)
for (int j=0;j<m;j++)
// 必须只有雷和0(包括.)
if (Map[i][j]!='*' && Map[i][j]!='0' && Map[i][j]!='.')
{
flag=false;
break;
}
if (flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
return 0;
}
H题
题意:
形如[:|||:]的就是手风琴字符串,删除字符串s中字符,使其变成手风琴,如果可以输出长度。
分析:
判断能不能变成手风琴就是要确定'[' , ':' , ':' , ']'的位置p1,p2,p3,p4。
其中p4>p3>p2>p1
'[' 直接找最左边的,']' 直接找最右边的确定位置。
':' 就找 '[' 右边第一个和 ']' 左边第一个
然后再找p2-p3之间的 '|'
参考代码:
#include <cstdio>
#include <iostream>
#include <string>
using namespace std;
string s;
int p1=-1,p2=-1,p3=-1,p4=-1,ans=4;
int main()
{
cin >> s;
// 找最左边的'['位置
for (int i=0;i<s.length();i++)
{
if (s[i]=='[')
{
p1=i;
break;
}
}
// 没找到,输出-1
if (p1==-1)
{
cout << -1 << endl;
return 0;
}
// 找'['后第一个':'
for (int i=p1+1;i<s.length();i++)
{
if (s[i]==':')
{
p2=i;
break;
}
}
// 没找到,输出-1
if (p2==-1)
{
cout << -1 << endl;
return 0;
}
// 找最右边的']'
for (int i=s.length()-1;i>=0;i--)
{
if (s[i]==']')
{
p4=i;
break;
}
}
// 没找到或者在左侧':'左边,输出-1
if (p4==-1 || p4<=p2)
{
cout << -1 << endl;
return 0;
}
// 找右侧':'
for (int i=p4-1;i>=0;i--)
{
if (s[i]==':')
{
p3=i;
break;
}
}
// 没找到或者在左侧':'左边,输出-1
if (p3==-1 || p3<=p2)
{
cout << -1 << endl;
return 0;
}
// 找两个':'中间的'|'
for (int i=p2+1;i<=p3-1;i++)
if (s[i]=='|')
ans++;
cout << ans << endl;
return 0;
}
I题
题意:
这个题目就是给你n个范围[Li,Ri],然后把这些范围分成两堆(不能为空),每堆和另外一堆都不能有重合,最后输出每一个范围放在哪一堆(没有输出-1)。
分析:
我们先将这些范围按照L从小到大排序,然后依次放到堆1,堆2,先放1再放2,再放1,不断重复。其中如果放1或者2不可以的话就放回原来那堆。例如前一个范围放的是堆1,现在的范围本应该放堆2,但是放堆2会和堆1冲突,于是我们将其放回堆1(放了肯定不会产生冲突)。依次类推直到放完所有的,如果最后有一个堆为空就说明不存在解,输出-1。
参考代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
struct node
{
int l,r;
int id;
}a[100005];
//lmax控制堆1的最大值,rmax控制堆2的最大值
int ans[100005],lmax,rmax;
//按照L升序,L相同R小的在前
int cmp(node a1,node a2)
{
if (a1.l==a2.l)
return a1.r<a2.r;
else
return a1.l<a2.l;
}
//flag1标记堆1不为空,flag2标记堆2不为空
int T,n,flag1,flag2;
int main()
{
cin >> T;
while(T--)
{
lmax=rmax=0; flag1=flag2=0;
scanf("%d",&n); a[0].id=2;
//记录id
for (int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].l,&a[i].r);
a[i].id=i;
}
sort(a+1,a+n+1,cmp);
for (int i=1;i<=n;i++)
{
//放堆1
if (i==1 || ans[a[i-1].id]==2)
{
//放堆1不冲突
if (a[i].l>rmax)
{
ans[a[i].id]=1;
//重要!!!一开始只写了a[i].r,而没有考虑到a[i].r可能小于a[i-1].r
lmax=max(lmax,a[i].r);
flag1=1;
}
//冲突,放堆2
else
{
ans[a[i].id]=2;
rmax=max(rmax,a[i].r);
flag2=1;
}
}
//放堆2
else
{
//放堆2不冲突
if (a[i].l>lmax)
{
ans[a[i].id]=2;
rmax=max(rmax,a[i].r);
flag2=1;
}
//冲突,放堆1
else
{
ans[a[i].id]=1;
lmax=max(lmax,a[i].r);
flag1=1;
}
}
}
//两堆都有
if (flag1 && flag2)
{
for (int i=1;i<=n;i++)
printf("%d ",ans[i]);
printf("\n");
}
//只有1堆
else
printf("-1\n");
}
return 0;
}
J题
题意:
数学题,题目很简单,问敏敏学姐能不能逃走。
分析:
这题是队友出的,我只是个数论渣渣啊。
老师肯定是在圆上绕圈圈的,然后老师的角速度就是v2/R。
然后学姐可以在某个Rx上和老师一起保持同样的速度绕圈圈,这个时候学姐的角速度是v1/Rx=v2/R。
于是乎,一起绕圈圈的时候学姐的Rx=v1*R/v2。
然后呢学姐现在如果和老师一起绕圈圈他最近可以距离圈圈的边缘(R-Rx),即和老师保持最远距离(R+Rx),此时老师,中心和学姐是在一条直线上的。
然后现在就是学姐能不能逃走的关键了,如果她能在最短距离(R-Rx)里逃走那就成功了,此时老师要绕半个圆来抓学姐。
于是学姐的时间就是:(R-Rx)/v1
老师绕圈的时间就是:(pi*R)/v2,tips:这个pi有很多方法计算,也可以手写多写几位
比较一下两个时间就知道学姐能不能逃脱了,嘿嘿。
参考代码:
#include <cstdio>
#include <iostream>
#include <cmath>
const double pi=atan(1.0)*4;
using namespace std;
double R,v1,v2,Rx,t1,t2;
int main()
{
while(cin >> R >> v1 >> v2)
{
Rx=v1*R/v2;
t1=(R-Rx)/v1;
t2=(pi*R)/v2;
if (t1<=t2)
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}
K题
题意:
哦这题就是防ak的,其实我也不咋会。
分析:
好像是个贪心来着。
参考代码:
这里是大佬的解答:
posted on 2019-02-02 19:05 Radium_1209 阅读(272) 评论(0) 编辑 收藏 举报