420 likes | 571 Views
发散思维. 2008 年 11 月 16 日 主讲 :赵冬雪. 个人简介. 赵冬雪 ( 假装透明 ) 2005 级计算机 5 班 2007 年加入 ACM 现属队伍 TJU_BUDDHA Email: zdx1110@gmail.com BBS ID: zhaodongxue. TOJ 2076 . The Drunk Jailer. 题目描述:
E N D
发散思维 2008年11月16日 主讲 :赵冬雪
个人简介 • 赵冬雪(假装透明) • 2005级计算机5班 • 2007年加入ACM • 现属队伍TJU_BUDDHA • Email: zdx1110@gmail.com • BBS ID: zhaodongxue
TOJ 2076 . The Drunk Jailer • 题目描述: • 监狱有n间牢房,看守喝醉了。首先,他打开所有的牢房。之后,他锁上序号为偶数的牢房(2,4,6……)。之后,对于序号为3的倍数的牢房,若牢房已锁则打开它;否则锁上它。……整个过程一共进行n次,最后看守回去睡觉。此时,如果牢房开着,犯人将逃跑。 • 问一共会跑掉多少个犯人?
Sample Input 2 5 100 Sample Output 2 10
直觉想法——模拟 复杂度O(n^2) int n, ans; bool state[n +1]; /*0表示锁上,1表示打开*/ int i; for(i = 1; i <= n; i += i) { if(state[i] == 1) state[i] = 0; else state[i] = 1; } for(i = 1, ans = 0; i <= n; i ++) ans += state[i]; cout << ans << endl;
发散思维 • 每间牢房最后状态与它被访问过的次数有关 • 奇数次则牢房开着,偶数次则牢房锁上 • 所以,每间牢房最后状态只与它的因子数有关 • x = a * b 或者 x = a * a 其中 a <= x , b <= x • 只有平方数会被访问奇数次 • 只需求1~n的平方数 • 只需将n开根号取整 ans = (int)sqrt(n); • 复杂度 O( 1 ) ;
TOJ 1445. Ants • 题目描述 • 横杆上有n只蚂蚁在爬,杆长为l。已知蚂蚁的初始位置,但不知道蚂蚁的初始方向。一旦蚂蚁到达杆尽头,它就会掉下去。若两只蚂蚁相撞,他们同时改变方向继续爬。所有蚂蚁速度都为1. • 求:最后一只蚂蚁掉下去的最迟时间和最早时间。
Sample Input 2 10 3 2 6 7 214 7 11 12 7 13 176 23 191 Sample Output 4 8 38 207
思维转换 • 所有蚂蚁速度都一样 && 相撞之后转向 • 可以理解为: 两只蚂蚁相遇后继续按照原方向原速度爬行 • 计算每只蚂蚁掉下去所需的最大时间和最小时间——该蚂蚁与杆两端的距离
int pos[n]; int big = small = 0; int x, y, tmp; for(i = 0; i < n; i ++) { x = pos[i]; y = l – pos[i]; if(x > y) {tmp = x; x = y; y = tmp;} if(x > small) small = x; if(y > big) big = y; } cout << small << “ ” << big << endl;
TOJ 1065. Factorial • 题目描述 • 给定N的值(1 ≤ N ≤ 1000 000 000 ) • 计算 N ! 末尾 0 的个数
Sample Input 6 3 60 100 1024 23456 8735373 Sample Output 0 14 24 253 5861 2183837
题目分析 • 如果暴力计算,时间与空间都不允许 • 可以转而分析0出现的条件 • 2 * 5 = 10 • 转而计算2和5因子出现的次数 • 转而计算5出现的次数 • 5,10, 15, 20, 25, 30,35, 40…… • 需要注意:25, 50, 75, 100,125, ……
5……25……50……75……100……125 5 5 5 5 5 5 5 5 5 5 5 5 • ans = 0; • ans += n / 5; • ans += n / 25; • ans += n / 125; • ans += n / 625; • ……
ans = 0; while(n) { ans += n / 5; n = n / 5; } cout << ans << endl;
TOJ 3023. Give Me an E • 数字的英文表示中,很多含有e • 给定一个n,计算第n个不含e的数字 • Note: 1,001,001,001,001,001,001,001,001,000 is "one octillion, one septillion, one sextillion, one quintillion, one quadrillion, one trillion, one billion, one million, one thousand" • 所有答案都小于1028
Sample Input 1 10 838 0 Sample Output 2 44 4,002,064
含有e的数字 • one, two, three, four, five, • six, seven, eight, nine, ten, • eleven, twelve, thirteen, fourteen, fifteen, • sixteen, seventeen, eighteen, nineteen, • twenty, thirty, forty, fifty, sixty • seventy, eighty, ninety
1000以内不含e的数字 • 2 4 6 • 30 32 34 36 • 40 42 44 46 • 50 52 54 56 • 60 62 64 66 • 1000 • 共20个
cnt = 0; while(n) { ans[cnt ++] = rem[n % 20]; n /= 20; } if(cnt > 7) { cnt = 10; ans[9] = ans[7]; ans[7] = ans[8] = 0; }
if(cnt == 1) printf("%d\n", ans[0]); else { printf("%d", ans[cnt-1]); for(i = cnt-2; i >= 0; i --) printf(",%03d", ans[i]); printf("\n"); }
TOJ 2852. Bad Hair Day • 约翰大叔有N头牛,所有牛同向朝东站成一排 • 每头牛有一个固定身高 • 每头牛 i 能看见她前边的比她矮的牛的头顶,她共能看见 c [ i ] 头牛 • 求,c[1],c[2],……c[N]的总和
Sample Input 6 10 3 7 4 12 2 Sample Output 5 Hint: 0 + 3 + 0 + 1 + 0 + 1 + 0 = 5
图示 = Cow#1 #2, 3, 4 = = Cow#2 = = = Cow#3 #4 = = = ---> Cow#4 = = = = = Cow#5 # 6 = = = = = = Cow#6 1 2 3 4 5 6
使用栈!! • 1 从最后一头牛开始,依次考虑每一头牛i • 2 如果她的身高比栈顶牛身高大,出栈。转2 • 3 如果她的身高不比栈顶牛身高大,计算两只牛标号的差,即为c[i]。该牛标号进栈。转1 • 4 所有c[i]的总和即为最后所求。
sum = 0; top = 0; stack[top] = n-1; for(i = n-2; i >= 0;i --) { while(top>=0 && h[i] > h[stk[top]]){ top --;} if(top>=0)sum+=stack[top]-i-1; else sum+=n-1-i; top++; stk[top]=i; }
2440. Bridge over a rough river • 题目描述 • N个人晚上过桥。桥只能承担两个人的重量。过桥必须携带手电筒。手电筒只有一个。一直每个人过桥所需的时间。若两个人同时过桥,他们所需的时间为较慢的人的时间。 • 问:N个人全部通过需要多少时间?
Sample Input 4 6 7 6 5 Sample Output 29
传统想法:最快的人带着手电筒来回跑 • 分析其正确性: • N = 1, 2, 3, 正确 • N = 4,设他们过桥时间为a, b, X, Y 其中 a <= b <= X <= Y 总时间为a + a + b + X + Y • 是否可以更快呢?
消去X • 让X和Y一起过桥 • 所需时间为a + b + b + b + Y • 与上一结果a + a + b + X + Y比较 • 大小取决与 a + X 和 b + b 的大小,需讨论 • N > 4 ???
每次都将最慢的两个人送过桥 • 之后考虑剩下的N-2个人 • 直到N < 4 • N < 4 时分类讨论
int f ( int n) { int res; if(n == 1) return t[0]; if(n == 2) return t[1]; if(n == 3) return t[0] + t[1] + t[2]; if(2 * t[1] > t[0] + t[n-2]) res = t[0] + t[0] + t[n-2] + t[n-1]; else res = t[0] + t[1] + t[1] + t[n-1]; res += f(n - 2); return res; }
TOJ 1123. Counterfeit Dollar • 题目描述 • Sally 有一打银币,但是只有其中的11枚是真的。假币的重量与真币不同,但是她并不知道假币是更轻还是更重。她借来一个天平,可以使用3次。已知3次的结果,问假币是哪一枚 • 题目保证结果唯一,银币用字母A~L表示
Input: 1 ABCD EFGH even ABCI EFJK up ABIJ EFGH even Output: K is the counterfeit coin and it is light. 样例
题目分析 • 把case的形式全部转换成 even或up的形式(down可以转换成up,交换下天秤的左右) • 在上边的基础上,进行下边的讨论: • 满足以下两条件的字符,就是答案
条件一 • 情况1: 0个even 假币在天秤的同侧出现3次,也就是说这字符出现了3次 • 情况2: 1个even 假币在天秤的同侧出现2次,字符出现2次 • 情况3: 2个even 假币在天秤的同侧出现1次,字符出现1次 • 条件二:字符不出现在even的字串中
TOJ 2959. SREDNJI • 题目描述 • 数列A中有N个整数,分别为1~N • A的子串表示:只从A的头部和尾部删除x个数字(x可以是0)后所剩下的数列 • 数列A的中位数是B表示,在长度为奇数的数列A中,排序后B恰好在正中间。 • 已知数列长度N(1 ≤ N ≤ 100 000 )和数字B(1 ≤ B ≤ N ),求: • A有多少个子串的中位数是B?
Sample Input #1 5 4 1 2 3 4 5 Sample Input #2 6 3 1 2 4 5 6 3 Sample Input #3 7 4 5 7 2 4 3 1 6 Sample Output #1 2 Sample Output #2 1 Sample Output #3 4 样例
序号 0 1 2 3 4 5 6 7 • 数值 5 7 2 4 3 1 6 • 大于 0 1 2 2 2 2 2 3 • 小于 0 0 0 1 1 2 3 3 • + • + + + • + + + + + • + + + + + + +
差 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 ----------------------------------------------------------------- 前 0 0 0 0 0 0 0 1 2 1 0 0 0 0 0 * 后 0 0 0 0 0 0 1 2 1 0 0 0 0 0 0 --------------------------------------------------------------- 积 0 0 0 0 0 0 0 2 2 0 0 0 0 0 0 变 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
big[0] = sml[0] = 0; for(i = 1; i <= N; i ++){ big [i] = big [i-1]; sml [i] = sml [i-1]; if(a[i] < B) big[i] ++; else if(a[i] > B) sml [i] ++; } for(i = 0; i < q; i ++) bef[big[i] - sml [i] + N] ++; for(i = q; i <= N; i ++) aft[big[i] - sml [i] + N] ++; for(res = i = 0; i < 2 * N + 1; i ++) res += bef[i] * aft[i];
下课 Have Fun!!! o(∩_∩)o