您的位置:首页 > 理论基础 > 计算机网络

2018 BNUZ IT 节 ACM程序设计竞赛网络赛题解

2018-04-03 12:09 267 查看

A.   欧几里德的微笑

这道题要做到在一个二维空间上放置三个点,问这三个点是否能绕某个旋转点转一定角度后,a到b的位置,b到c的位置。
解法其实很简单,既然要求a到b,b到c,那么必然是点 a 到点 b 的距离要等于点b到点c 的距离的,这样它们才能够对称,并且不能三点共线就可以了,可以看作是以这个三角形做一个外切圆的原理。
代码:#include<bits/stdc++.h>
using namespace std;
#define ll long long

struct point{
ll x,y;
};

ll getDis(point p1,point p2){
return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);
}

int main(){
point p1,p2,p3;
int cas = 1;
int T;
scanf("%d",&T);
while(T--){
printf("Case #%d: ",cas++);
scanf("%lld %lld %lld %lld %lld %lld",&p1.x,&p1.y,&p2.x,&p2.y,&p3.x,&p3.y);
ll d1 = getDis(p1,p2);
ll d2 = getDis(p2,p3);;
if(d1 != d2){
puts("No");
}else if((p2.x - p3.x) * (p1.y - p2.y) == (p1.x - p2.x) * (p2.y - p3.y)){
puts("No");
}else{
puts("Yes");
}
}
}

B. 猫叔的计算器

这道题就是一个模拟计算器的操作,不过是计算机中的整数运算规则,在写法上也没有什么困难的,这道题的特点就是有的人写的特别长,有的人写的特别短
下面贴上最短的小Q同学的代码:#include <bits/stdc++.h>

#define ll long long

using namespace std;

int main() {
int T, cas = 1;
scanf("%d", &T);
while (T--) {
ll ans = 0, tmp;
scanf("%d", &ans);
getchar();
char c;
while ((c = getchar()) != '\n') {
scanf("%lld", &tmp);
if (c == '+') {
ans += tmp;
} else if (c == '-') {
ans -= tmp;
} else if (c == '*') {
ans *= tmp;
} else {
ans /= tmp;
}
}
printf("Case #%d:%lld\n", cas++, ans);
}
}

C.switch

这道题是说有n个小镇,其中有k个是有仓库的,现在要在一个没有仓库的小镇上开实体店,要求至少可到达一个仓库,让求到达仓库的最小距离
这道题其实是逗你玩得,根本就不需要什么最短路算法,那些写了Dijkstra的同学们被耍了…
因为它要求一定要到达仓库,那么如果能够到达仓库,最近的一定是与仓库直接相邻的,所以对于输入进来的数据,我们只要标记下所有的仓库,然后去找与仓库相邻的小镇,找到最小值就可以了。
代码:#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
#define ll long long
#define mem(a,x) memset(a,x,sizeof(a))

const ll inf = 1e9 + 1;

struct node{
int v;
ll w;
node(int vv = 0,ll ww = 0ll):v(vv),w(ww){}
};

int flag[maxn];

vector<node>edge[maxn];

void init(int n){
for(int i = 0;i <= n;i++){
edge[i].clear();
flag[i] = 0;
}
}

void add(int u,int v,ll w){
edge[u].push_back(node(v,w));
}

int main(){
int t,n,m,k,u,v,cas = 1;
ll w;
scanf("%d",&t);
while(t--){
scanf("%d %d %d",&n,&m,&k);
init(n);
for
4000
(int i = 1;i <= m;i++){
scanf("%d %d %lld",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
for(int i = 1;i <= k;i++){
scanf("%d",&u);
flag[u] = 1;
}
ll ans = inf;
for(int i = 0;i <= n;i++){
if(flag[i] == 1){
int sz = edge[i].size();
for(int j = 0;j < sz;j++){
if(flag[edge[i][j].v] == 0){
ans = min(ans,edge[i][j].w);
}
}
}
}
if(ans == inf){
printf("Case #%d: -1\n",cas++);
}else{
printf("Case #%d: %lld\n",cas++,ans);
}
}
return 0;
}

D. 未来日记

这道题原本是一道防AK题,但是无奈同学们的检索功力太强,都找到了原题,并且是一毛一样的…这怪我…
言归正传,这道题是一道经典Tarjan算法,也就是求强连通分量的算法,不懂强连通的同学可以自行百度一波
整体思路就是用Tarjan求出有多少个强连通分量,那么只要把这些强连通分量用最少的边连起来,就成了一个完整的强连通分量,就满足所有人可达了。求出强连通分量后,在每个强连通分量之间统计出度和入度,一个强连通分量至少要有一个出度和入度,只要每个强连通分量都有一个出度和一个入度,就一定可以构成一个完整的强连通分量了。
统计总共缺多少出度和多少入度,取大的那个值作为答案(因为有可能有好几个强连通分量指向了一个强连通分量 比如只有ABC三个点的图,线索有 A->B   C->B 的这种,总共却一个出度和两个入度,那么答案肯定是需要两个线索的)(即可多不可少)
代码:#include <bits/stdc++.h>

using namespace std;

#define mem(a,x) memset(a,x,sizeof(a))
#define ll long long
#define maxn 50005

struct edge {
int nxt,to;
} e[maxn];

int low[maxn],dfn[maxn],vis[maxn],head[maxn],pre[maxn],out[maxn],in[maxn];
int dep,num,cnt,n,m;
stack<int>sta;
void add(int u,int v) {
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt++;
}
void tarjan(int u){
dfn[u] = low[u] = ++dep;
sta.push(u);
vis[u] = 1;
for(int i = head[u];i != -1;i = e[i].nxt){
int v = e[i].to;
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u],low[v]);
}else if(vis[v]){
low[u] = min(low[u],dfn[v]);
}
}
int v;
if(low[u] == dfn[u]){
num++;
do{
v = sta.top();
sta.pop();
pre[v] = num;
vis[v] = 0;
}while(u != v);
}
}

void init() {
cnt = num = dep = 0;
while(!sta.empty())
sta.pop();
mem(head,-1);
mem(low,0);
mem(dfn,0);
mem(vis,0);
mem(pre,0);
mem(out,0);
mem(in,0);
}

int main() {
int u,v,Case = 1,T;
scanf("%d",&T);
while(T--) {
scanf("%d %d",&n,&m);
init();
for(int i = 1; i <= m; i++) {
scanf("%d %d",&u,&v);
add(u,v);
}
for(int i = 1; i <= n; i++) {
if(!dfn[i])
tarjan(i);
}
if(num == 1)
printf("Case #%d: 0\n",Case++);
else {
for(u = 1; u <= n; u++) {
for(int i = head[u]; i != -1; i = e[i].nxt) {
v = e[i].to;
if(pre[u] != pre[v]) {
out[pre[u]]++;
in[pre[v]]++;
}
}
}
int ans1 = 0,ans2 = 0;
for(int i = 1; i <= num; i++) {
if(in[i] == 0)
ans1++;
if(out[i] == 0)
ans2++;
}
printf("Case #%d: %d\n",Case++,max(ans1,ans2));
}
}
return 0;
}

E. 寻找旅馆的学长

水题一枚,写两个 cmp 就好了,然后根据最后输入的是0 还是 1 选择cmp进行一个排序就ok了
代码:#include<bits/stdc++.h>
using namespace std;
struct node{
int m;
int s;
}datas[100000];

bool cmp1(node data1,node data2) {
if(data1.s != data2.s)
return data1.s < data2.s;
else
return data1.m < data2.m;
}

bool cmp2(node data1,node data2) {
if(data1.m != data2.m)
return data1.m < data2.m;
else
return data1.s < data2.s;
}
int main() {
int t,m,s,f;
while(~scanf("%d",&t)) {
for(int i = 1; i <= t; i++) {
int count = 0;
while(scanf("%d %d",&m,&s)&&(m||s)) {
datas[count].m = m;
datas[count++].s = s;
}

scanf("%d",&f);
if(f) {
sort(datas,datas+count,cmp1);
} else {
sort(datas,datas+count,cmp2);
}
cout<<"Case #"<<i<<":"<<endl;
for(int j = 0; j < 3; j++)
cout<<datas[j].m<<" "<<datas[j].s<<endl;
}
}
return 0;

F. 命运石之门的选择

这是一道裸广搜题,只需要在广搜的过程中根据他是α世界线分支或是β世界线分支控制好跳跃路径经可以了,真的很裸很暴力
代码:#include <bits/stdc++.h>

#define MAXN 100005
#define pii pair<int, int>
#define mp(a,b) make_pair(a, b)

using namespace std;

int a[MAXN];
queue<pii> q;
bool mark[MAXN];
int n;

bool check(int i) {
if (i >= 0 && i <= n && !mark[i]) {
mark[i] = true;
return true;
}
return false;
}

int bfs(int s, int k) {
memset(mark, false, sizeof(mark));
while (!q.empty()) {
q.pop();
}
q.push(mp(s, 0));
while (!q.empty()) {
int u = q.front().first;
int t = q.front().second;

mark[u] = true;

if (u == k) {
return t;
}
q.pop();
if (a[u]) {
if (check(u - 2)) {
q.push(mp(u - 2, t + 1));
}
if (check(u + 2)) {
q.push(mp(u + 2, t + 1));
}
if (check(u * 2)) {
q.push(mp(u * 2, t + 1));
}
} else {
if (check(u - 1)) {
q.push(mp(u - 1, t + 1));
}
if (check(u + 1)) {
q.push(mp(u + 1, t + 1));
}
}
}
return -1;
}

int main() {
int T, cas = 1;
scanf("%d", &T);
int s, k;
while (T--) {
scanf("%d %d %d", &n, &s, &k);
for (int i = 0; i <= n; i++) {
scanf("%d", &a[i]);
}
printf("Case #%d: ", cas++);
printf("%d\n", bfs(s, k));
}
}

G. 细胞分裂

签到题
2^n 只要注意 pow 出来的结果强转为longlong,否则会有精度丢失
代码:#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define ll long long
using namespace std;
const ll mod = 1e9+7;
int main(){
ll T,n;
int cas = 1;
scanf("%lld",&T);
while(T--){
scanf("%lld",&n);
ll ans = pow(2,n);
printf("Case #%d: %lld\n",cas++,ans);
}
return 0;
}H. 四月是你的谎言
一道没什么可讲只是教你使用STL的模拟题
下面贴上依旧是最短的小Q的代码:#include<bits/stdc++.h>

#define MAXN 105

using namespace std;

map<string, int> title;

bool mark[MAXN][35];
queue<int> q[MAXN][35];

void init() {
memset(mark, false, sizeof(mark));
title.clear();
for (int i = 0; i < 105; i++) {
for (int j = 0; j < 35; j++) {
while (!q[i][j].empty()) {
q[i][j].pop();
}
}
}
}

int main() {
int T, x, n, cas = 1;
string y;
int Q, tmp;
scanf("%d", &T);
while (T--) {
printf("Case #%d:\n", cas++);
init();
int cnt = 0;
scanf("%d", &Q);
while (Q--) {
getchar();
char opt;
scanf("%c", &opt);
if (opt == 's') {
cin >> x >> y;
if (!title[y]) {
title[y] = ++cnt;
}
mark[x][title[y]] = 1;
} else if (opt == 'p') {
cin >> y >> n;
if (!title[y]) {
title[y] = ++cnt;
}
int id = title[y];
for (int i = 0; i < n; i++) {
scanf("%d", &tmp);
for (int j = 0; j < MAXN; j++) {
q[j][id].push(tmp);
}
}
} else {
cin >> x >> y >> n;
int id = title[y];
if (!mark[x][id]) {
puts("No Subscription");
continue;
}
bool flag = false;
while (!q[x][id].empty() && n--) {
if (flag) {
printf(" ");
}
printf("%d", q[x][id].front());
q[x][id].pop();
flag = true;
}
puts("");
}
}
}
}

I. 聪明的学长

一道水题,找第k大的数,暴力做法排序之后从后往前找,但是不推荐
建议使用分治法
但是只给出暴力代码:#include<bits/stdc++.h>
using namespace std;

bool cmp(int a,int b) {
return a > b;
}

int main() {
int t,n,id;
int num[100000];
while(~scanf("%d",&t)) {
for(int i = 1; i <= t; i++) {
scanf("%d",&n);
for(int j = 0; j < n; j++) {
scanf("%d",&num[j]);
}
scanf("%d",&id);
cout<<"Case #"<<i<<": ";
if(id > n)
cout<<"-1"<<endl;
else {
sort(num,num+n,cmp);
cout<<num[id-1]<<endl;
}

}
}

return 0;
}

J. 猫叔的烦恼

一个二维平面上的涂点题,由于数据不大,输入完后只要把会覆盖目标点的地毯找出来,最后一个就是最上面的,简单粗暴
代码:#include <bits/stdc++.h>

#define MAXN 100005

using namespace std;

struct node {
int x, y, h, w;
} a[MAXN];

int main() {
int T, n, cas = 1;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d %d %d %d", &a[i].x, &a[i].y, &a[i].h, &a[i].w);
}
int x, y;
int ans = -1;
scanf("%d %d", &x, &y);
for (int i = 0; i < n; i++) {
if (a[i].x <= x && a[i].x + a[i].h >= x
&& a[i].y <= y && a[i].y + a[i].w >= y) {
ans = i + 1;
}
}
printf("Case #%d: %d\n", cas++, ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: