Sat May 20 12:55:34 CST 2017
回归acm集训队第一场组队赛!不管队友是谁,团队协作+不懈努力!gangbadei!
SDUT 2017 春夏组队训练赛3
Sat May 20 21:44:29 CST 2017
解题+补题: L_Z_S
SDUT 2017 春夏组队训练赛3 C D F I J L
C - Shooting Game
来源:FZU - 2144
在小队成员一同分析过后,结论这是一道贪心题,类似时间管理。然而代码过程频频出错,还是经验不足啊!总结题的要点有:
1.数学过程有大坑!
-每个输入数据都能用int来存,但是解方程过程中大量乘法易爆出范围(用对每一个乘法运算先×1.0解决了);
-输入作为有效数据计入的条件分析不很到位;
-测评系统不支持long long 的 %lld形式输入(%I64d可行,FOJ的特点?);
2.对数据的分析判断,一定要好好结合题意!凭感觉不分析没好事。。
ac代码1:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <math.h>
#define N 100100
using namespace std;
struct info
{
double s;
double e;
} mo[N];
bool cmp(struct info a, struct info b)
{
return a.e <= b.e;
}
int main ()
{
int t, kase = 0;
int n;
long long r;
long long dx, dy, dz;
long long x, y, z;
long long a, b, c;
double ch;
cin >> t;
while(t--)
{
cin >> n >> r;
int l = 0;
for(int i = 0; i < n; i++)
{
scanf("%I64d %I64d %I64d", &x, &y, &z); //若用%lld会tle,神奇!
scanf("%I64d %I64d %I64d", &dx, &dy, &dz);
a = dx * dx + dy * dy + dz * dz;
b = 2 * (x * dx + y * dy + z * dz);
c = x * x + y * y + z * z - r * r;
ch = 1.0 * b * b - 4 * a * c;
if(ch >= 0)
{
double al, be;
al = - ((b + sqrt(ch)) / 2 / a);
be = - ((b - sqrt(ch)) / 2 / a);
if (be >= 0)
mo[l].s = al, mo[l++].e = be;
}
}
sort(mo, mo + l, cmp);
int sum = 0;
if (l)
{
sum++;
double time = mo[0].e;
for(int i = 1; i < l; )
{
while(i < l && mo[i].s <= time)
i++;
if (i < l)
sum++;
time = mo[i].e;
}
}
printf("Case %d: %d %d\n", ++kase, l, sum);
}
return 0;
}
ac代码2:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <math.h>
#define N 100100
using namespace std;
struct info
{
double s;
double e;
} mo[N];
bool cmp(struct info a, struct info b)
{
return a.e <= b.e;
}
int main ()
{
int t, kase = 0;
int n, r;
int dx, dy, dz;
int x, y, z;
double a, b, c;
double ch;
cin >> t;
while(t--)
{
cin >> n >> r;
int l = 0;
for(int i = 0; i < n; i++)
{
scanf("%d %d %d", &x, &y, &z);
scanf("%d %d %d", &dx, &dy, &dz);
a = 1.0 * dx * dx + 1.0 * dy * dy + 1.0 * dz * dz; //cause: may overflow during every multiplication operation
b = 2 * (1. * x * dx + 1. * y * dy + 1. * z * dz);
c = 1. * x * x + 1. * y * y + 1. * z * z - 1. * r * r;
ch = b * b - 4 * a * c;
if(ch >= 0)
{
double al, be;
al = - ((b + sqrt(ch)) / 2 / a);
be = - ((b - sqrt(ch)) / 2 / a);
if (be >= 0)
mo[l].s = al, mo[l++].e = be;
}
}
sort(mo, mo + l, cmp);
int sum = 0;
if (l)
{
sum++;
double time = mo[0].e;
for(int i = 1; i < l; )
{
while(i < l && mo[i].s <= time)
i++;
if (i < l)
sum++;
time = mo[i].e;
}
}
printf("Case %d: %d %d\n", ++kase, l, sum);
}
return 0;
}
F - A-B Game
FZU - 2147
知识点:某数a的余数最大时对应除数为:a / 2 + 1 ( / 为整除)。
此题wa了好久,最后把所有输入从scanf改为cin就对了,神奇!%FZU!
当然有知道为什么的大神,求留言!
(貌似和上题同一个毛病!?%lld不可用)
wa:
#include <stdio.h>
#include <string.h>
int main(void)
{
long long a, b;
int t, c;
scanf("%d", &t);
int kase = 0;
while (t--)
{
scanf("%lld %lld", &a, &b);
c = 0;
while (a > b)
{
a = a / 2 + 1;
c++;
}
printf("Case %d: %d\n", ++kase, c);
}
return 0;
}
ac:
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int main(void)
{
long long a, b;
int t, c;
cin >> t;
int kase = 0;
while (t--)
{
cin >> a >> b;
c = 0;
while (a > b)
{
a = a / 2 + 1;
c++;
}
printf("Case %d: %d\n", ++kase, c);
}
return 0;
}
果真!!下面是ac代码
#include <stdio.h>
#include <string.h>
int main(void)
{
long long a, b;
int t, c;
scanf("%d", &t);
int kase = 0;
while (t--)
{
scanf("%I64d %I64d", &a, &b);
c = 0;
while (a > b)
{
a = a / 2 + 1;
c++;
}
printf("Case %d: %d\n", ++kase, c);
}
return 0;
}
I - Moon Game
FZU - 2148
题意:一个平面上给出多个不重点,且任意三点不在一条直线上,求能作出的凸四边形(convex quadrilateral)个数。
解法一:四点中任取两点作直线,之后再取另两点作直线,若两次操作余下两点均在直线的同侧或均在异侧,则为凸四边形,否则为凹四边形;作个图就明了了。
解法二:将四个点两两相连,得到四个三角形。若是从凹四边形判断则有
SΔABC=SΔABD+SΔACD+SΔBCD
;若是从凸四边形判断则有
SΔABD+SΔCBD=SΔBAC+SΔDAC
;面积则用海伦公式求得。
(具体实现后面再补)
J - Fire Game
FZU - 2150
这是一道简单暴力搜索题,但原谅我们半天没怼出方案来,自己挖了坑跳不出来了。其中有趣的想法是:
L:连通分量+最短路(硬是没想出只有一个连通分量时怎么做,局限在单点开始的bfs中…);
S:生成树的”最长路“(忽略了这是地图,是有贿赂(回路)阻挠的!图 != 树)
解法:两起点一同入队bfs。
下面是我自己优化过的ac代码,但可能仍有可改进的地方:
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <algorithm>
using namespace std;
typedef struct Posi
{
int x, y;
}Grid;
char Map[15][15];
int book[15][15]; //相当于vis[]和访问次序记录数组
int dx[4] = {1, 0, 0, -1}; //四方向
int dy[4] = {0, 1, -1, 0};
int n, m;
int bfs(int, int, int, int);
int main(void)
{
int t, kase = 0;
scanf("%d", &t);
while (t--)
{
memset(Map, 0, sizeof(Map));
scanf("%d %d ", &n, &m);
for (int i = 1; i <= n; i++)//从1开始,和memset配合,节省了对边界的特殊判断~
{
char tmp[15];
scanf("%s", tmp);
for (int j = 1; j <= m; j++)//同上
Map[i][j] = tmp[j - 1];
}
int ans = 100;
for (int i = 1; i <= n; i++) //四重for
for (int j = 1; j <= m; j++)
{
if (Map[i][j] == '#')
{
for (int k = i; k <= n; k++) //k = 1优化,去重省了近一半时间(原time:187ms)
for (int l = 1; l <= m; l++) //l = j不可行
{
if (Map[k][l] == '#')
{
int tmp = bfs(i, j, k, l);
if (tmp < ans)
ans = tmp;
}
}
}
}
if (ans >= 100)
ans = -1;
printf("Case %d: %d\n", ++kase, ans);
}
return 0;
}
int bfs(int x, int y, int a, int b)
{
memset(book, -1, sizeof(book));
Grid Queue[112]; //手工简单队列,方便快捷省时间~
int head = 0, tail = 0;
Queue[0].x = x, Queue[0].y = y;
book[x][y] = 0;
if (x != a || y != b) //防止同一起点重复入队(对于这个代码不判断也行)
{
Queue[1].x = a, Queue[1].y = b;
tail++;
book[a][b] = 0;
}
int ans = -1;
while (head <= tail)
{
int f = 0;
int tx = Queue[head].x;
int ty = Queue[head].y;
for (int i = 0; i < 4; i++)
{
int fx = tx + dx[i];
int fy = ty + dy[i];
if (Map[fx][fy] == '#' && book[fx][fy] == -1)
{
f = 1;
book[fx][fy] = book[tx][ty] + 1;
Queue[++tail].x = fx;
Queue[tail].y = fy;
}
}
if (!f && ans < book[tx][ty])
ans = book[tx][ty];
++head;
}
for (int i = 1; i <= n; i++) //判断是否走完所有'#'
for (int j = 1; j <= m; j++)
{
if (Map[i][j] == '#' && book[i][j] == -1)
{
ans = 1000;
break;
}
}//可优化省去,只在主函数里出现一次两层for,但貌似对时间没什么影响
return ans;
}//time:93ms
下面是和Shang讨论后得到的用queue实现的代码,优化了一些地方,但queue的用时保持在了300ms左右。(或许有更好的,待mark)
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <algorithm>
using namespace std;
typedef struct node
{
int x;
int y;
}Node;
char Map[12][12];
int vis[12][12]; //同时记录访问次序
Node Grid[110]; //存储每个草地位置,目的把四重for简化为两重
int dx[4] = {1, 0, 0, -1};
int dy[4] = {0, 1, -1, 0};
int bfs(int u, int v); //u、v表示草地编号
int n, m;
int l; //用于记录草地总数
int main()
{
int t;
scanf("%d", &t);
int cnt = 0;
while(t--)
{
memset(Map, 0, sizeof(Map));
scanf("%d%d", &n, &m);
l = 0;
for(int i = 1; i <= n; i++)
{
scanf("%s", Map[i] + 1); //能想到的最快的输入方法,mark!
for(int j = 1; j <= m; j++)
{
if(Map[i][j] == '#')
{
Grid[l].x = i;
Grid[l++].y = j;
}
}
}
int Min = 9999;
for(int i = 0; i < l; i++)
{
for(int j = i; j < l; j++)
{
memset(vis, -1, sizeof(vis));
Min = min(bfs(i, j), Min);
}
}
printf("Case %d: %d\n", ++cnt, Min >= 9999 ? -1 : Min);
}
return 0;
}
int bfs(int u, int v)
{
queue <Node> q;
vis[Grid[u].x][Grid[u].y] = 0;
q.push(Grid[u]);
if (u != v)
{
vis[Grid[v].x][Grid[v].y] = 0;
q.push(Grid[v]);
}
int ans = -1;
int sum = 0; //记录烧过的草地数
while (!q.empty())
{
Node tmp = q.front();
q.pop();
sum++;
int f = 1;
for (int i = 0; i < 4; i++)
{
int nx = tmp.x + dx[i];
int ny = tmp.y + dy[i];
if (Map[nx][ny] == '#' && vis[nx][ny] == -1)
{
f = 0;
vis[nx][ny] = vis[tmp.x][tmp.y] + 1;
Node next = {nx, ny};
q.push(next);
}
}
if (f && ans < vis[tmp.x][tmp.y]) //对路尽头进行处理
ans = vis[tmp.x][tmp.y];
}
if (sum < l) //判断是否全部走完
ans = 9999;
return ans;
}
D、L是水题,略。
总结这一次的组队赛,自己还有许多待完成的事,并无成功与否之分,但有所悟有所成长。