NEERC 2016 Central Subregional Contest Editorial (Gym 101243)

A - Fried Fish

题意

有 n 条鱼,每条鱼均需要煎两面;每一时刻可以同时煎 k 条鱼。问最少需要多久能煎完所有鱼?

解题思路

注意点:一条鱼的两面不能同时煎。

故 k 最大不能超过 n ,否则多余的应闲置。

答案 = 2×nk

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int n, k;
    scanf("%d %d", &n, &k);
    if(k > n)   k = n;
    printf("%d\n", (2*n+k-1) / k);
}

B - Hanoi Tower

题意

汉诺塔模型,将 N (N % 3 == 0) 个盘中从 A 柱移动到 B 柱的过程中,第一次出现 A, B, C 三根柱上的盘数相同的时候,移动了多少步?

解题思路

通过理解汉诺塔的递归过程及模拟,可以发现:

ans[n] = dp[n/3*2-1] + dp[n/3-1] * (n%2?2:1) + 1 . 其中 dp[i] 表示将 n 个盘从 A 柱移动到 B 柱的步数 (dp[i]=dp[i-1]*2+1dp[1]=1

代码

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.math.BigInteger;

/**
 * Created by Utop on 2017/6/28.
 */
public class Main {

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new FileReader("input.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"));

        BigInteger[] dp = new BigInteger[200];
        dp[0] = BigInteger.ZERO;
        dp[1] = BigInteger.ONE;
        for (int i=2;i<200;i++) {
            dp[i] = dp[i-1].multiply(BigInteger.valueOf(2)).add(BigInteger.ONE);
        }

        int n = Integer.parseInt(br.readLine());
        BigInteger ans = dp[n/3*2-1].add(dp[n/3-1]).add(BigInteger.ONE);
        if (n % 2 == 1) {
            ans = ans.add(dp[n/3-1]);
        }
        bw.write(ans.toString());
        bw.flush();
        bw.close();
    }

}

C - Desktop

题意

2×2 的图标铺满 H×W 的桌面,要求可以重叠(后放入的图标可以部分遮盖原有图标),但每个图标必须至少露出面积为 2 ,同时要求图标的顶点必须落在整数坐标上。

解题思路

构造,按照 H 或 W 为偶数边长的边进行扩展,例如:

H 为偶数,则每个坐标的左上顶点坐标为

(1, 1), (1,2), (1,3), …, (1, w-2);

(3, 1), (3, 2), (3, 3), …, (3, w-2);

(h-1, 1), (h-1, 2), (h-1, 3), …, (h-1, w-2);

若 H 不为偶数但 W 为偶数,类似构造。

当 H , W 均为奇数时,优先构造最后一行,构造方案为 (h-1, 1), (h-1, 3), (h-1, 5), …, (h-1, w-3). 之后可将问题看作是 (H1)×W

代码

#include<bits/stdc++.h>
using namespace std;
int h, w;
bool rev = false;
vector<pair<int, int> > ans;
int main()
{
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    scanf("%d %d", &h, &w);
    if(h < 2 || w < 2)  printf("0\n");
    else
    {
        if(h%2 == 1 && w%2 == 1)
        {
            for(int col=1;col<w;col+=2)
                ans.push_back(make_pair(h-1, col));
            h--;
        }
        if(h%2 == 0)
        {
            for(int row=1;row<=h;row+=2)
                for(int col=1;col<=w-2;col++)
                    ans.push_back(make_pair(row, col));
            for(int row=1;row<h;row++)
                ans.push_back(make_pair(row, w-1));
        }
        else if(w%2 == 0)
        {
            for(int col=1;col<=w;col+=2)
                for(int row=1;row<=h-2;row++)
                    ans.push_back(make_pair(row, col));
            for(int col=1;col<w;col++)
                ans.push_back(make_pair(h-1, col));
        }
        printf("%d\n", ans.size());
        for(int i=0;i<ans.size();i++)
            printf("%d %d\n", ans[i].first, ans[i].second);
    }
}

D - Weather Station

题意

仅包含 N, E, S, W 的字符串。其中有效字符子串为 NE,N,NW,W,SW,S,SE,E . 问字符串有多少合法分割串。

解题思路

简单的动态规划,状态转移为:

dp[i] = dp[i-1] + jug()*dp[i-2] , 其中 jug() 为判断第 i-1,i 位字符能够组成有效字符子串,是为 1, 否则为 0.

代码

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
char s[100010];
long long dp[100010] = {1, 1};
bool jug(char a, char b) {
    if(b == 'N' || b == 'S')    return false;
    if(a == 'S' && b != 'S')    return true;
    if(a == 'N' && b != 'N')    return true;
    return false;
}
int main()
{
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    scanf(" %s", s+1);
    int len = strlen(s+1);
    for(int i=2;i<=len;i++)
        (dp[i] = dp[i-1] + jug(s[i-1], s[i]) * dp[i-2]) %= mod;
    printf("%I64d\n", dp[len]);
}

E - Cupcakes

题意

一队 N 个人循环排队吃蛋糕,初始时刻蛋糕有 K 个,每个人每次轮到队首时吃 [1, a[i]] 个(其中令 a[i] 最大的唯一一人为 Greedy Guy ,每次他必定吃 min(a[i], 剩余蛋糕数) 个)。问 N-1 个人 (除 Greedy Guy) 能否通过控制自己吃的蛋糕数量使得轮到 Greedy Guy 时刚好没有蛋糕?

解题思路

此题暴力贪心即可。每次维护在 [l, r] 表示当轮到 Greedy Guy 时最少及最多能吃掉多少蛋糕。当 l<=k && k<=r 时,表示可以控制当轮到 Greedy Guy 时没有蛋糕。

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int n, a[100010], mx = 0;
    long long k, l = 0, r = 0, round_l = 0, round_r = 0;
    scanf("%d %I64d", &n, &k);
    for(int i=1;i<=n;i++)
        scanf("%d", &a[i]), mx = max(mx, a[i]);
    for(int i=1;a[i] != mx;i++)
        l += 1, r += a[i];
    for(int i=1;i<=n;i++)
        if(a[i] != mx)
            round_l += 1, round_r += a[i];
    while(l <= k)
    {
        if(l <= k && k <= r) {
            printf("YES\n");    return 0;
        }
        l += mx + round_l,  r += mx + round_r;
    }
    printf("KEK\n");
}

F - Vitamins

题意

要求区分三种不同重量的药丸,White 最重,Red 次之,Blue 最轻。已知有 N 颗并标号 1, 2, 3…, N 。给出 M 组比较结果:

  • i < j ,表示编号 i 药丸比编号 j 轻
  • i = j ,表示 i , j 为同一种类
  • i > j ,表示编号 i 药丸比编号 j 轻

要求按序给出最终每颗药丸的判断结果 W, R , B,若无法判断,则为 ?

解题思路

利用并查集将可判定的同类药丸归类。

由于 W > BR > B ,以及 W > R ,故在所有药丸未知的情况下,只能通过获得 i < j && i > k 判断 i 为 R ,其余 WB 无法提前判断。

在尽可能判断出 R 后,通过将其根据比较关系扩展即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1000 + 10;
int n, m, fa[N], up[N], dn[N];
char arr[N], op;
vector<int> st[N];
vector<pair<int, int> > v;
int find(int x) {   return fa[x] = fa[x] == x ? x : find(fa[x]);    }
void Union(int x, int y) {
    int fx = find(x),   fy = find(y);
    if(fx == fy)    return;
    fa[fx] = fy;
}
int main()
{
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    scanf("%d %d", &n, &m);
    for(int i=1;i<=n;i++)   fa[i] = i,  arr[i] = '?';
    for(int i=1, a, b;i<=m;i++)
    {
        scanf("%d%c%d", &a, &op, &b);
        if(op == '=')   Union(a, b);
        else {
            if(op == '<')   swap(a, b);
            v.push_back(make_pair(a, b));
        }
    }
    for(int i=1;i<=n;i++)
    {
        find(i);
        st[ fa[i] ].push_back(i);
    }
    for(int i=0, a, b;i<v.size();i++)
    {
        a = v[i].first, b = v[i].second;
        dn[ find(a) ]++;
        up[ find(b) ]++;
    }
    for(int i=1;i<=n;i++)
    {
        if(up[i] && dn[i])
        {
            for(int j=0, idx;j<st[fa[i]].size();j++)
            {
                idx = st[ fa[i] ][j];
                arr[idx] = 'R';
            }
            st[ fa[i] ].clear();
        }
    }
    for(int i=0, a, b;i<v.size();i++)
    {
        a = v[i].first, b = v[i].second;
        if( arr[a] == 'R' )
        {
            for(int j=0;j<st[fa[b]].size();j++)
                arr[ st[ fa[b] ][j] ] = 'B';
            st[fa[b]].clear();
        }
        else if( arr[b] == 'R' )
        {
            for(int j=0;j<st[fa[a]].size();j++)
                arr[ st[ fa[a] ][j] ] = 'W';
            st[fa[a]].clear();
        }
    }
    printf("%s\n", arr+1);
}

G - Sphenic numbers

题意

判断 N 是否由三个不同素数的乘积。

解题思路

简单质因数分解。

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int n, fac[50], exp[50], pos=0, sqt;
    scanf("%d", &n);
    sqt = sqrt(n * 1.0);
    for(int i=2;i<=sqt;i++)
    {
        if(n%i == 0) {
            fac[++pos] = i;
            exp[pos] = 0;
            while(n % i == 0)
                exp[pos]++,
                n /= i;
        }
    }
    if(n!=1) {
        fac[++pos] = n;
        exp[pos] = 1;
    }
    for(int i=1;i<=3;i++)
        if(exp[pos] > 1)    pos = -1;
    printf("%s\n", pos==3 ? "YES" : "NO");
}

H - Non-random numbers

题意

根据给定的 N ,求满足下列条件的数的个数:

  • 含有 N 个数字的正整数
  • 这个正整数不能含有前导零
  • 第 i 个数字不能出现数字 i (从左到右)

解题思路

ans = 8×9×...×989×10×...×10n-910 ,当 n > 10 时。其余不表。

代码

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.math.BigInteger;

/**
 * Created by Utop on 2017/6/28.
 */
public class Main {

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new FileReader("Input.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("Output.txt"));

        int n = Integer.parseInt(br.readLine());
        BigInteger ans = BigInteger.ONE;

        for (int i=1;i <= Math.min(n, 9); i++ ) {
            ans = ans.multiply(BigInteger.valueOf(i==1?8:9));
        }

        for (int i=10;i <= n; i++ ) {
            ans = ans.multiply(BigInteger.valueOf(10));
        }

        bw.write(ans.toString());
        bw.flush();
        bw.close();
    }

}

I - Land Division

题意

对于给定的 N 条边的凸多边形,判断是否将其用一条线段分割为两块,每块的边数分别为 K 和 M。求最短的分割线段长度,若不能则输出 -1

解题思路

  • 若分割线通过凸多边形的两个顶点,则一定满足 K+M == N+2 。此时枚举任意点 i 与对应的点 (i+m-1)%n 的距离,求最小值。
  • 若分割线通过凸多边形的一个顶点与一条边,则一定满足 K+M == N+3 。此时枚举任意点 i 与对应的边的距离,求最小值。注意:此时求的是点到线段的距离,且应考虑以点 i 顺时针方向块为 K 边或以点 i 逆时针方向块为 M 。
  • 若分割线通过凸多边形的两条边,则满足 K+M == N+4 。此时枚举任意边 i 与其对应的边的距离,求最小值。对于任意两条线段的最短距离,可以求枚举四个端点到相对的线段的距离。
  • 否则,其必定无法构造。输出 -1

代码

#include<bits/stdc++.h>
using namespace std;
int sgn(double x) {
    if(abs(x) < 1e-8)   return 0;
    return x > 0 ? 1 : -1;
}
inline double sqr(double x) {   return x * x;   }
struct Point {
    double x, y;
    Point() {};
    Point(double a, double b): x(a), y(b) {};
    void input() {  scanf(" %lf %lf", &x, &y);  };
    friend Point operator+(const Point &a, const Point &b) {
        return Point(a.x + b.x, a.y + b.y);
    }
    friend Point operator-(const Point &a, const Point &b) {
        return Point(a.x - b.x, a.y - b.y);
    }
    double norm() {
        return sqrt(sqr(x) + sqr(y));
    }
} p[110];
double det(const Point &a, const Point &b) {
    return a.x * b.y - a.y * b.x;
}
double dot(const Point &a, const Point &b) {
    return a.x * b.x + a.y * b.y;
}
double dist(const Point &a, const Point &b) {
    return (a-b).norm();
}
struct Line {
    Point a, b;
    Line() {}
    Line(Point x, Point y): a(x), b(y) {};
} l[110];
double dis_point_segment(const Point p, const Point s, const Point t) {
    if( sgn(dot(p-s, t-s)) < 0) return (p-s).norm();
    if( sgn(dot(p-t, s-t)) < 0) return (p-t).norm();
    return abs(det(s-p, t-p) / dist(s, t));
}
int main()
{
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int n, m, k;
    scanf("%d %d %d", &n, &m, &k);
    for(int i=0;i<n;i++)
        p[i].input();
    double ans = 10000000;
    if(m+k == n+2) {
        for(int i=0;i<n;i++)
            ans = min(dist(p[i], p[(i+m-1)%n]), ans);
        printf("%.3lf\n", ans);
    } else if(m+k == n+3) {
        for(int i=0;i<n;i++)
            ans = min(dis_point_segment(p[i], p[(i+m-2)%n], p[(i+m-1)%n]), ans),
            ans = min(dis_point_segment(p[(i+m-1)%n], p[i], p[(i+1)%n]), ans);
        printf("%.3lf\n", ans);
    } else if(m+k == n+4) {
        for(int i=0;i<n;i++)
            l[i].a = p[i],
            l[i].b = p[(i+1)%n];
        for(int i=0;i<n;i++)
        {
            int j = (i+m-2) % n;
            ans = min(dis_point_segment(l[i].a, l[j].a, l[j].b), ans);
            ans = min(dis_point_segment(l[i].b, l[j].a, l[j].b), ans);
            ans = min(dis_point_segment(l[j].a, l[i].a, l[i].b), ans);
            ans = min(dis_point_segment(l[j].b, l[i].a, l[i].b), ans);
        }
        printf("%.3lf\n", ans);
    } else {
        printf("-1\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值