A
这么简单的题直接贴代码好了。
1 #include <cstdio> 2 #include <cmath> 3 using namespace std; 4 5 bool islucky(int a) 6 { 7 a = abs(a); 8 while(a) 9 { 10 if(a % 10 == 8) return true; 11 a /= 10; 12 } 13 return false; 14 } 15 16 int main(void) 17 { 18 int a, b = 1; 19 scanf("%d", &a); 20 while(!islucky(a + b)) b++; 21 printf("%d\n", b); 22 23 return 0; 24 }
代码君
B
题意:
有4个从小到大排列的正整数,x1 x2 x3 x4 ,他们满足下面三个数相等:
现在丢了4-n的数,只剩下其中的n个数(0≤n≤4)
问能否找到4-n个正整数,使得这四个数重新满足上面的条件。
分析:
这道题思路很简单,就是比较繁琐,看到有人写了100多甚至200行代码,所以我还是觉得有必要把我的思路详细分析一下的。
对条件变一下形,等价于下面两个条件:
- x4 = 3x1
- x1 + x4 = x2 + x3
我们对n不同的情况来考虑
- n=0,直接随便输出一组解就好了,比如 1 1 3 3
- n=1,比如所给的数为a, a a 3a 3a 就是一组解
- n=2,所给的数为a b(a ≤ b), 我们断言b ≤ 3a时,才有解,为 a b (4a-b) 3a 。 首先若b ≤ 3a,前面所给的解是满足条件的。 若 b > 3a,则四个数中最小的一定是a,最大的一定是b,然而根据我们的条件有x4 = 3x1,即b = 3a,导出矛盾,所以无解。
- n=3,设所给的三个数为a、b、c。我们只枚举有解的情况,其他情况则无解:
- 若c = 3a,则 a b (4a-b) 3a 是一组解
- 若c ≤ 3a 且 b + c = 4a, 这时 a b c 3a 是一组解
- 若c是3的倍数 且 a + b = , (c / 3) a b c,是一组解
- n=4直接判断是否满足题目要求条件就好了
代码也比较短,只有50多行。
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 int n, a[4], ans[4]; 6 7 bool solve() 8 { 9 switch(n) 10 { 11 case 0: 12 ans[0] = ans[1] = 1, ans[2] = ans[3] = 3; 13 return true; 14 case 1: 15 ans[0] = a[0], ans[1] = ans[2] = 3 * a[0]; 16 return true; 17 case 2: 18 if(a[1] <= a[0] * 3) 19 { 20 ans[0] = a[0] * 3; 21 ans[1] = a[0] * 4 - a[1]; 22 return true; 23 } 24 return false; 25 case 3: 26 if(a[2] == a[0] * 3) 27 { ans[0] = a[0] * 4 - a[1]; return true; } 28 if(a[2] <= a[0] * 3 && a[1] + a[2] == a[0] * 4) 29 { ans[0] = a[0] * 3; return true; } 30 if(a[2] % 3 == 0 && a[0] + a[1] == a[2]/3*4) 31 { ans[0] = a[2] / 3; return true; } 32 break; 33 case 4: 34 if(a[0] + a[3] == a[1] + a[2] && a[0] * 3 == a[3]) 35 return true; 36 } 37 return false; 38 } 39 40 int main(void) 41 { 42 scanf("%d", &n); 43 for(int i = 0; i < n; ++i) scanf("%d", &a[i]); 44 sort(a, a + n); 45 if(solve()) 46 { 47 printf("YES\n"); 48 for(int i = 0; i < 4-n; ++i) printf("%d\n", ans[i]); 49 } 50 else printf("NO\n"); 51 52 return 0; 53 }
代码君
C(暴力)
题意:
Master Yang 要去打败一个怪兽(Monster), 他和怪兽都有血量HP,攻击力ATK和防御力DEF。
每个时刻,怪兽使杨大师血量减少 max{0, ATKM - DEFY}, 同样地, 杨大师对怪兽造成的伤害为 max{0, ATKY - DEFM}
如果某一时刻HPY > 0 且 HPM ≤ 0,则视为将怪兽打败。
为了能够打败怪兽,杨大师可以去商店购买HP、ATK和DEF。
现给出杨大师和怪兽的HP、ATK和DEF,和购买每点HP、ATK和DEF所需要消耗的金币,求能够打败怪兽所需要的最少的金币。
分析:
因为数据范围很小,所以暴力是完全可以的。
我们枚举可能购买的攻击力和防御力,然后算出打败怪兽后的剩余HP,如果HP少于1,那么就要购买相应的补回来。然后取每次总花费的最小值。
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 int main(void) 6 { 7 int y[3], m[3], price[3], ans = 2000000000; 8 for(int i = 0; i < 3; ++i) scanf("%d", &y[i]); 9 for(int i = 0; i < 3; ++i) scanf("%d", &m[i]); 10 for(int i = 0; i < 3; ++i) scanf("%d", &price[i]); 11 12 for(int buyatk = 0; buyatk <= 200; ++buyatk) 13 for(int buydef = 0; buydef <= 200; ++buydef) 14 { 15 int crtatk = y[1] + buyatk; 16 int crtdef = y[2] + buydef; 17 int hurty = max(0, m[1] - crtdef); //杨大师收到的伤害 18 int hurtm = max(0, crtatk - m[2]); //怪兽收到的伤害 19 if(hurtm == 0) continue; //打不掉怪兽的血,跳过循环 20 int k = m[0] % hurtm == 0 ? m[0]/hurtm : m[0]/hurtm + 1; 21 int LeftHP = y[0] - hurty * k; 22 int cost = price[1] * buyatk + price[2] * buydef + (1 - LeftHP > 0 ? (1 - LeftHP) * price[0] : 0); 23 ans = min(ans, cost); 24 } 25 26 printf("%d\n", ans); 27 28 return 0; 29 }
代码君
D
像这种贪心贪不出来的都感觉是DP。
在CF上扒了一个46ms的代码,也总算是看懂了。
题意:
纸条上有n个数,可以将纸条剪为若干小段(只有一个数字也可以),使得每段上的数字满足:
- 最大值与最小值之差不超过s
- 数字个数不少于l
分析:
从第一个数开始向左右两个方向延伸(第一个数只能往右延伸),求得一个最大的闭区间[left1, right1],使得这个区间里的数都满足第一个条件 max - min ≤ s.
如果区间的长度小于l,则无解
如果长度大于等于l,我们则求出了第一段纸片右端点的一个范围[l, right1]
第二段纸片从第 right + 1 个数向两侧延伸,这时向左延伸就有了一个界限,因为要保证第一段纸片至少有l个数,所以第二段纸片最多只能向左延伸到第l+1个数。
记此时的区间为[left2, right2],同样地,看区间的长度是否≥l来判断是否有解。这样一来,为了保证在第三段纸片延伸的时候第二段至少有l个数,所以第三段纸片至多向左延伸到left2+l+1
所以我们用last来记录每次向左延伸的界限
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 const int maxn = 100000 + 10; 6 long long a[maxn], last = 0; 7 bool flag = true; 8 9 int main(void) 10 { 11 //freopen("Din.txt", "r", stdin); 12 13 long long n, s, l, ans = 0; 14 scanf("%I64d%I64d%I64d", &n, &s, &l); 15 for(int i = 0; i < n; ++i) scanf("%I64d", &a[i]); 16 17 long long pos, left, right; 18 for(pos = 0; pos < n; ++pos) //从pos位置开始延伸 19 { 20 long long maxm, minm, cnt = 1; //区间延伸过程中的最小值和最大值,以及区间中元素的个数 21 22 maxm = a[pos], minm = a[pos]; 23 right = pos + 1; 24 while(right < n) 25 { 26 maxm = max(maxm, a[right]); 27 minm = min(minm, a[right]); 28 if(maxm - minm <= s) cnt++; 29 else break; 30 right++; 31 } 32 right--; 33 34 maxm = a[pos], minm = a[pos]; 35 left = pos - 1; 36 while(left >= last) 37 { 38 maxm = max(maxm, a[left]); 39 minm = min(minm, a[left]); 40 if(maxm - minm <= s) cnt++; 41 else break; 42 left--; 43 } 44 left++; 45 46 if(cnt < l) 47 { 48 flag =false; 49 break; 50 } 51 ans++; 52 last = left + l; 53 pos = right; 54 } 55 56 if(!flag) ans = -1; 57 printf("%I64d\n", ans); 58 59 return 0; 60 }
代码君
E(数论+构造)
题意:
给出一个正整数n,问是否存在一个 1~n的排列,使得前i个数的乘积模上n的余数得到的序列是0~n-1的一个排列。
分析:
当n为1、4和素数的时候有解,构造方法是用 (i+1)*(i的逆元)%n 填充当前的数。
这道题先留着,等我比较系统地学习数论以后在给出严格证明以及代码。