SDUT 2017 春夏组队训练赛3

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是水题,略。
总结这一次的组队赛,自己还有许多待完成的事,并无成功与否之分,但有所悟有所成长。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值