正文
题目1
题目链接
题目大意:
小明有一只猫,现在猫的饥饿值为H,并且每分钟会增加D;
他可以选择现在就买猫粮,1份猫粮价格为C,可以减少猫的饥饿值N;(猫粮只能一份一份购买)
他也可以选择晚上20点之后购买,商店会打8折;(当前的时间为hh时mm分)
问,小明最少需要花费多少,才能把猫的饥饿值降到0;
输入:
第一行,hh and mm (00 ≤ hh ≤ 23, 00 ≤ mm ≤ 59)
第二行,H, D, C and N (1 ≤ H ≤ 1e5, 1 ≤ D, C, N ≤ 1e2).
输出:
小明最少的花费,精确到小数点4位。
Examples
input
19 00
255 1 100 1
output
25200.0000
题目解析:
从题目已经给出的条件我们可以判断,在没打折的时候,花费是(H+N-1)/N*C;
如果有打折,就要加上猫增加的饥饿值。
那么,我们容易得到一种方法:用max(0LL, 20 * 60 - hh * 60 - mm) * d计算猫增加的饥饿值,再统一计算价格;
再从cost1、cost2中选择一个较小值即可。
思考:
题目有个小trick,打折后的数字是浮点数。
int main(int argc, const char * argv[]) {
// insert code here...
lld hh, mm;
lld h, d, c, n;
cin >> hh >> mm;
cin >> h >> d >> c >> n;
lld cost1 = (h + n - 1) / n * c;
double cost2 = (max(0LL, 20 * 60 - hh * 60 - mm) * d + h + n - 1) / n * c * 0.8;
cout << min((double)cost1, cost2) << endl;
return 0;
}
题目2
题目链接
题目大意:
我们认为一个字符串是好看,如果它满足:
1、由两种字符组成;
2、调整字符的顺序,可以使得相同字符是连续的;
比如说:ab、aba、abb、aaabbbb这样的字符是好看的,但abc、aa这样的字符是不好看的;
现在给出字符串s,问能否把s分成两个子序列,使得子序列都可以组成好看的字符串;
输入:
第一行,字符串s (1 ≤ |s| ≤ 1e5)
输出:
如果可以,输出Yes;否则输出No;
Examples
input
ababa
output
Yes
input
yeee
output
No
题目解析:
好看的字符串,其实就是要求字符串有且只有两种字符串。
题目的意思是把字符串s分成两个字符串,并且每个都有两种字符。
那么先统计字符串s中各个字符的数量,得到字符种数num,如果:
num>4,无解;
num=4,有解,直接分为2+2;
num=3,必须有一个字符的数量>=2;
num=2,两个字符数量都满足>=2;
num=1,无解;
思考:
用a[i]表示字符串i的数量,然后对a进行从大到小的排序,可以简洁写完上面的判断
if ((count == 4) || (count == 3 && a[0] >= 2) || (count == 2 && a[1] >= 2)) cout << "Yes" << endl;
char str[N];
int a[N];
bool cmp(int a, int b) {
return a > b;
}
int main(int argc, const char * argv[]) {
// insert code here...
cin >> str;
long n = strlen(str);
int count = 0;
for (int i = 0; i < n; ++i) {
if (a[str[i] - 'a'] == 0) ++count;
a[str[i] - 'a']++;
}
sort(a, a + 26, cmp);
if ((count == 4) || (count == 3 && a[0] >= 2) || (count == 2 && a[1] >= 2)) {
cout << "Yes" << endl;
}
else {
cout << "No" << endl;
}
return 0;
}
题目3
题目链接
题目大意:
小明邀请了n个朋友过来吃披萨,披萨只有一个且是圆形的;
现在需要把披萨分成形状完全相同的n+1块,问需要至少切多少刀?
输入:
第一行:n,表示朋友人数 ( 0 ≤ n ≤ 1e18 )
输出:
至少需要切多少刀。
Examples
input
3
output
2
样例解释:
两刀垂直切,得到4块。
题目解析:
由题目意思知道,一个圆切分成n+1块,那么每个扇形的弧度是2π/(n+1);
那么按照这个弧度平分,不过圆心的切,(n+1)刀必定可以平分;
有另外一种情况,是过圆心切,比如样例的情况,两刀可以切成4块。
在这种切法情况下,切线经过的扇形,分成两部分。
即是说:
如果是分成奇数块,那么不能用过圆心的切法(因为每次都是切成偶数块);
如果是分成偶数块,那么可以只用过圆心的切法;
于是有:
(n+1)是奇数,ans=(n+1);
(n+1)是偶数,ans=(n+1)/2;
思考:
trick1:n很大,如果用int会爆;
trick2:n=0的时候,ans=0;
int main(int argc, const char * argv[]) {
// insert code here...
lld n;
cin >> n;
if (n == 0) {
cout << 0 << endl;
return 0;
}
++n;
if (n % 2) {
cout << n << endl;
}
else {
cout << n / 2 << endl;
}
return 0;
}
题目4
题目链接
题目大意:
"Kuro", "Shiro" , "Katie"三个人在玩游戏;
每个人一个字符串,长度相同;
游戏有n轮,每轮每个人都必须修改其中一个字符为其他任意字符;
字符串中的任意子串subStr,在字符串重复出现的次数,就是每个人的得分。
问游戏结束后,谁的分数最高;
输入:
第一行:n,游戏轮数 ( 0 ≤ n ≤ 1e9 )
接下来3行字符串,分别表示"Kuro", "Shiro" , "Katie"的字符串;
输出:
输出最高分的人,"Kuro", "Shiro" , "Katie";
如果有两个最高分,则输出'Draw';
Examples
input
3
Kuroo
Shiro
Katie
output
Kuro
题目解析:
任意子串的最大重复次数,其实就是某个字符的最大出现次数,因为:
如果ab是最大重复次数的子串,那么a出现的次数不会比ab少;
那么游戏转换为对字符串进行最多n次操作,使得某个字符出现最大次数。
容易知道,假如已经出现字符次数最大的字符是x,那么相当于把非x的字符改成x。
那么有sum=min(len, count+n); sum是字符串的最大重复子串出现次数,len是字符串长度,count是相同字符出现最大次数;
接着分别判断三个人的得分即可。
思考:
trick:字符串是全部相同的字符,且n=1的时候! 此时最多是len-1
int main(int argc, const char * argv[]) {
// insert code here...
int n;
cin >> n;
string str[3];
cin >> str[0] >> str[1] >> str[2];
int sum[3];
for (int i = 0; i < 3; ++i) {
sum[i] = 0;
int count[1000]={0};
for (int j = 0; j < str[i].length(); ++j) {
count[str[i][j]]++;
sum[i] = max(sum[i], count[str[i][j]]);
}
if (sum[i] == str[i].length() && n == 1) {
sum[i] = str[i].length() - 1;
}
else {
sum[i] = min((int)str[i].length(), sum[i] + n);
}
}
int totalMax = max(sum[0], max(sum[1], sum[2]));
int size = 0, ans = 0;
for (int i = 0; i < 3; ++i) {
if (sum[i] == totalMax) {
++size;
ans = i;
}
}
if (size > 1) {
cout << "Draw" << endl;
}
else {
if (ans == 0) {
cout << "Kuro" << endl;
}
else if (ans == 1) {
cout << "Shiro" << endl;
}
else {
cout << "Katie" << endl;
}
}
return 0;
}
题目5
题目链接
题目大意:
Kuro生活在一个有n个小镇的国家,n个小镇由n-1条路相连,任意两个小镇都可以通过这些路相通。
n个小镇里面有两个特殊的小镇a和b,小镇a养了很多花,小镇b养了很多蜜蜂;
如果先经过小镇a,再经过小镇b,就会在小镇b吸引到很多蜜蜂,造成很严重的后果。
Kuro想定一条旅游路线(u,v)从小镇u到小镇v,每个小镇只能经过一次;(但不能先经过小镇a,再经过小镇b)
问,有多少条可能的路线?(u到v和v到u是不同的路线)
输入:
第一行:n , x and y,表示小镇数量和小镇a、b ( 1 ≤ n ≤ 3 * 1e5 , 1 ≤ x , y ≤ n , x ≠ y )
接下来n-1行,每行两个数字u,v 表明u和v之间存在相通。
输出:
可能的路线数量。
Examples
input
3 1 3
1 2
2 3
output
5
题目解析:
如果不考虑小镇a、b的影响,那么有n*(n-1)的路线;(因为题目给出来的是一棵树,并且小镇不能重复经过)
那么先经过小镇a,再经过小镇b的路线有哪些呢?
小镇a到小镇b的路只有一条,这一条路把n个小镇分成两部分,小镇a部分(假设数量是sumA) 和 小镇b部分(假设数量是sumB)。
那么先经过a再经过b的路线有sumA*sumB。
那么ans=n*(n-1)-sumA*sumB。
vector g[N];
int vis[N];
lld cnt[N];
lld dfs(lld u) {
vis[u] = 1;
lld sum = 1;
for (int i = 0; i < g[u].size(); ++i) {
lld v = g[u][i];
if (!vis[v]) {
sum += dfs(v);
}
}
return cnt[u] = sum;
}
int main(int argc, const char * argv[]) {
// insert code here...
lld n, x, y;
cin >> n >> x >> y;
for (int i = 1; i < n; ++i) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
lld ans = n * 1LL * (n - 1);
vis[x] = 1;
for (int i = 0; i < g[x].size(); ++i) {
lld v = g[x][i];
dfs(v);
if (vis[y]) { // 这个子树包括y
lld sumA = n - cnt[v];
lld sumB = cnt[y];
ans -= sumA * sumB;
break;
}
}
cout << ans << endl;
return 0;
}
总结
题目1,模拟题,注意到题目的隐藏条件,当时间延后的时候会增加饥饿值;
题目2,分类讨论,根据字符串中字符种数,再根据不同情况进行处理;
题目3,思考题,根据题目给出了重要线索“形状完全相同”和圆的特性,推演出来每一块的形状,自然可以根据奇、偶数量进行分类讨论;
题目4,模拟题,要特别注意trick,就是当n=1并且字符完全相同时;(这种也是不太喜欢的题目,容易踩坑)
题目5,逆向思考,题目看似很复杂,但是有一个隐含条件“任意两点之间只有一条路线”,这是由题目给出的条件退出来的,当我们发现求经过a再经过b的数量更好算时,就可以通过总数减去a经过b的数量来计算符合要求的路线数量。