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+1
且 dp[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). 之后可将问题看作是 (H−1)×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 > B
且 R > B
,以及 W > R
,故在所有药丸未知的情况下,只能通过获得 i < j && i > k
判断 i 为 R
,其余 W
和 B
无法提前判断。
在尽可能判断出 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×...×98个9×10×...×10n-9个10
,当 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");
}
}