算法分析习题课 第三章 李承乾
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
算法分析习题选讲(第三章)
李承乾 498727460@qq.com
第三章
• • • • • • • • • • • 1152 1153 马周游 1093 Air Express 1134 积木分发 1140 国王的遗产 1438 Shopaholic 1028 Hanoi Tower Sequence 1029 Rabbit 1381 a*b 1206 1012 Stacking Cylinders 1172 Queens, Knights and Pawns 1034 Forest
第三章
• • • • • • • • • • • 1193 Up the Stairs 1004 I Conduit! 1017 Rate of Return 1059 Exocenter of a Trian 1003 Hit or Miss 1018 A Card Trick 1052 Candy Sharing Game 1041 Pushing Boxes 1211 商人的宣传 1071 Floors 1082 MANAGER
1134 积木分发
• 一个人手上有s块积木,一共有n个小朋友,每 个小朋友手上有a块积木,还需要b块积木才能 完成。当一个小朋友的积木完成后则全部回收 利用。问是否所有小朋友都能完成。 • s<=1000000,n<=10000,a,b<=10^9
解题思路
• 尽量先满足需求少的小朋友,以回收得到更多积 木; • 因此按小朋友的需求排序,贪心求解。
解题思路
• 尽量使价格高的东西免费; • 按价格从高到低排序后,每三件取一件免 费; • sort(price,price+n); • for (i=n-3;i>=0;i-=3) • s+=price[i];
1028 Hanoi Tower Sequence
• 定义汉诺塔,共有三个柱子和很多的大小 两两不同的盘子放在一个柱子上,要求把 它们移到另一个柱子上,每次只能移动一 个放在柱子最顶端的盘子,并且每次移动 后需保证较小的盘子在较大的盘子上面。 给出步数p,求第p步移动的盘子的大小。 • p<=10^100
主过程
1381 a*b
• 给出两个整数a和b,求a*b的结果。(题目 描述有误,可能有零) • 0<=a<=10^100,0<=b<=10000。
解题思路
• • • • 高精度,模拟竖式乘法。 从低位向高位逐位计算。 输出时注意结果为0的情况。 另外,这两个大整数程序都以10为进位, 实际使用时可以用10000为进位数,把4个 数字压在一个数组中,可以明显提高程序 效率。
• int cal(int a[],int n) { • int cnt=1; • while (a[n-1]%2==0) { • cnt++; • for (int i=0,temp=0;i<n;i++) { • temp=temp*10+a[i]; • a[i]=temp/2; • temp%=2; • } • } • return cnt; • }
• • • • • • • • • • • • •
struct child { int a,b; }; bool operator <(const child &x,cont child &y) { return x.b<y.b; } bool check(child children[],int s,int n) { sort(children, children+n); for (int i=0;i<n;i++) { if (s<children[i].b) return false; s+=children[i].a; } return true; }
解题思路
• 最小运输价格必定出现在:
– 1、不添加任何重量; – 2、添加重量刚好到达某个区间的下界;
• 因此枚举这些情况,并求出其中的最小值 即是答案。
• int cal(int weight) { • int price=INF; • for (int i=0;i<4;i++) { • if (weight>=l[i]&&weight<=r[i]) { • if (weigth*rate[i]<price) • price=weight*rate[i]; • } else if (weight<l[i]) { • if (l[i]*rate[i]<price) • price=l[i]*rate[i]; • } • } • return price; • }
1140 国王的遗产
• n块金块组成一棵树,总共k个人,每个人 选择树里的一条边去掉,得到不超过当前 金块的一半的部分,并且分割使自己得到 尽量多的金块,如果相等则使金块组编号 最小。输出每个人得到的金块数量。 • n<=30000,k<=100
解题思路
• 枚举每个人的时候,检查切断每一条边所 得到的金块数和组编号,并得到最大金块 数和最小组编号。 • 切断每条边后,得到一棵子树,可计算得 到子树节点数和最小编号; • 若每次从最小编号节点开始递归,则可以 轻易比较子树的组编号以及它的补图的组 编号。
11Байду номын сангаас2 1153 马周游
• 题目大意: • 一个有限大小的棋 盘上有一只马,马 只能按日字方式走 ,如图所示。 • 给出初始时马的位 置,找出一条马移 动的路线,经过所 有格子各一次。
解题思路
• 枚举马能走的所有路径, • 直至找到一条完成周游的路径; • 递归,回溯。
• bool solve(int x, int y, int lev) { • route[lev] = x * N + y; • if (lev == M * N - 1) {print_route();return true;} • visited[x][y] = true; • grid grids[8]; • int n=get_grid(grids,x,y); • for (i=0; i<n; i++) • if (solve(grids[i].x, grids[i].y, lev+1)) • return true; • visited[x][y] = false; • return false; • }
优化
• 以上程序速度过慢。 • 优化:改变搜索顺序。
– 先搜索可行格较少的格子。 – 其他顺序。
• 修改get_grid()函数。
• int get_grid(grid grids[], int x,int y) { • int n=0; • for (int i=0; i<8; i++) { • int xx = x + direction[i][0] • int yy = y + direction[i][1]; • if (xx>=0&&yy>=0&&xx<M&&yy<N • &&!visited[xx][yy]) { • grids[n].x = xx; grids[n].y = yy; • grids[n].count = get_count(xx, yy); • n++; • } • } • sort(grids,grids+n); • return n; • }
• bool operator < (const grid &a, const grid &b) { • return a.count < b.count; • } • int get_count(int x, int y) { • int i, xx, yy, count = 0; • for (i=0; i<8; i++) { • xx = x + direction[i][0]; • yy = y + direction[i][1]; • if (xx>=0&&yy>=0&&xx<M&&yy<N&&!visited[xx][y y]) • count++; • } • return count; • }
•
• • •
point operator / (double b){return point(x/b,y/b);}
point operator * (double b){return point(x*b,y*b);} double dis(){return sqrt(x*x+y*y);} void left(){y=-y;swap(x,y);}
1093 Air Express
• 给出4个重量区间以及在每个区间的单位重量运输 价格,再给出一个背包重量,可以往背包里添加 任意多重量,求最低的总运输价格。 • 所有数字不超过1000。 • Package weight Cost per pound • 0 to 9 pounds $10 • 10 to 49 pounds $5 • 50 to 99 pounds $3 • 100 pounds or more $2
1029 Rabbit
• 题目大意: • 开始有一对大兔子。每对大兔子每个月产 生一对小兔子。每对小兔子经过m个月变成 大兔子。问经过d个月后有多少兔子。 • 1<=m<=10, 1<=d<=100
解题思路
• 当m=1时,每个月的兔子数量是上个月的 两倍,经过d=100个月兔子将有2^100个兔 子,超过64位整数表示的范围,因此要用 高精度。 • 每个月的兔子数量,为上一个月的兔子数 量,加上上一个月的大兔子数量,即m个月 前的兔子数量,即a[n]=a[n-1]+a[n-m]。 • 注意当n-m为负数时,也有初始时存大的一 对大兔子。
1206 1012 Stacking Cylinders
• 如图所示,给出最底 层的n个球的位置,求 最顶层的球的位置。 • 1<=n<=10
解题思路
• 关键点在于已知两个圆的圆心坐标,求放 在这两个圆上的圆的圆心坐标。 • 向量+勾股定理。
• struct point • { • double x,y; • • • • point(){x=0;y=0;} point(double a,double b){x=a;y=b;} point operator - (point b){return point(x-b.x,y-b.y);} point operator + (point b){return point(x+b.x,y+b.y);}
解题思路
• n个盘子的汉诺塔问题递归求解:把前n-1 个盘子移到第二根柱子上,把第n个盘子移 到第三根柱子上,把前n-1个盘子移到第三 根柱子上。 • 从一根柱子到另一根柱了,移动1个盘子需 要f(1)=1步,移动k(k>1)个盘子需要f(k)=f(k1)+1+f(k-1)步。 • 即得到f(k)=2^k-1。因此第2^k步移动的是 k+1个盘子。在移动第k+1个盘子后,左右 对移地移动k个盘子
• int get_grid(grid grids[], int x,int y) { • int n=0; • for (int i=0; i<8; i++) { • int xx = x + direction[i][0]; • int yy = y + direction[i][1]; • if(xx>=0&&yy>=0&&xx<M&&yy<N • &&!visited[xx][yy]) { • grids[n].x = xx; • grids[n].y = yy; • n++; • } • } • return n; • }
• 记录子树的大小,删 除的边的两个端点, 子树中的最小编号
• 用vector来保存树的边
不同子树之间的比较
主过程
• 找出树中的最小编号,从该编号开始DFS
1438 Shopaholic
• 买东西每买三件东西则最便宜的一件免费 。给出n个需要买的东西的价格,问最多能 免费多少? • n<=20000,价格不超过20000
• 把p化成二进制数,假设现在p里有超过1个 位为1,设最高位为k,则在2^k步前和2^k 步后对称,因此第p步移的盘子与第p-2^k 步移的盘子一样。 • 直到p里只有1个位为1,设此时p=2^m,则 此步移动的是第m+1个盘子。 • 因此题目转化成二进制数p,从低位起连续 0的位数,加1,为答案。
李承乾 498727460@qq.com
第三章
• • • • • • • • • • • 1152 1153 马周游 1093 Air Express 1134 积木分发 1140 国王的遗产 1438 Shopaholic 1028 Hanoi Tower Sequence 1029 Rabbit 1381 a*b 1206 1012 Stacking Cylinders 1172 Queens, Knights and Pawns 1034 Forest
第三章
• • • • • • • • • • • 1193 Up the Stairs 1004 I Conduit! 1017 Rate of Return 1059 Exocenter of a Trian 1003 Hit or Miss 1018 A Card Trick 1052 Candy Sharing Game 1041 Pushing Boxes 1211 商人的宣传 1071 Floors 1082 MANAGER
1134 积木分发
• 一个人手上有s块积木,一共有n个小朋友,每 个小朋友手上有a块积木,还需要b块积木才能 完成。当一个小朋友的积木完成后则全部回收 利用。问是否所有小朋友都能完成。 • s<=1000000,n<=10000,a,b<=10^9
解题思路
• 尽量先满足需求少的小朋友,以回收得到更多积 木; • 因此按小朋友的需求排序,贪心求解。
解题思路
• 尽量使价格高的东西免费; • 按价格从高到低排序后,每三件取一件免 费; • sort(price,price+n); • for (i=n-3;i>=0;i-=3) • s+=price[i];
1028 Hanoi Tower Sequence
• 定义汉诺塔,共有三个柱子和很多的大小 两两不同的盘子放在一个柱子上,要求把 它们移到另一个柱子上,每次只能移动一 个放在柱子最顶端的盘子,并且每次移动 后需保证较小的盘子在较大的盘子上面。 给出步数p,求第p步移动的盘子的大小。 • p<=10^100
主过程
1381 a*b
• 给出两个整数a和b,求a*b的结果。(题目 描述有误,可能有零) • 0<=a<=10^100,0<=b<=10000。
解题思路
• • • • 高精度,模拟竖式乘法。 从低位向高位逐位计算。 输出时注意结果为0的情况。 另外,这两个大整数程序都以10为进位, 实际使用时可以用10000为进位数,把4个 数字压在一个数组中,可以明显提高程序 效率。
• int cal(int a[],int n) { • int cnt=1; • while (a[n-1]%2==0) { • cnt++; • for (int i=0,temp=0;i<n;i++) { • temp=temp*10+a[i]; • a[i]=temp/2; • temp%=2; • } • } • return cnt; • }
• • • • • • • • • • • • •
struct child { int a,b; }; bool operator <(const child &x,cont child &y) { return x.b<y.b; } bool check(child children[],int s,int n) { sort(children, children+n); for (int i=0;i<n;i++) { if (s<children[i].b) return false; s+=children[i].a; } return true; }
解题思路
• 最小运输价格必定出现在:
– 1、不添加任何重量; – 2、添加重量刚好到达某个区间的下界;
• 因此枚举这些情况,并求出其中的最小值 即是答案。
• int cal(int weight) { • int price=INF; • for (int i=0;i<4;i++) { • if (weight>=l[i]&&weight<=r[i]) { • if (weigth*rate[i]<price) • price=weight*rate[i]; • } else if (weight<l[i]) { • if (l[i]*rate[i]<price) • price=l[i]*rate[i]; • } • } • return price; • }
1140 国王的遗产
• n块金块组成一棵树,总共k个人,每个人 选择树里的一条边去掉,得到不超过当前 金块的一半的部分,并且分割使自己得到 尽量多的金块,如果相等则使金块组编号 最小。输出每个人得到的金块数量。 • n<=30000,k<=100
解题思路
• 枚举每个人的时候,检查切断每一条边所 得到的金块数和组编号,并得到最大金块 数和最小组编号。 • 切断每条边后,得到一棵子树,可计算得 到子树节点数和最小编号; • 若每次从最小编号节点开始递归,则可以 轻易比较子树的组编号以及它的补图的组 编号。
11Байду номын сангаас2 1153 马周游
• 题目大意: • 一个有限大小的棋 盘上有一只马,马 只能按日字方式走 ,如图所示。 • 给出初始时马的位 置,找出一条马移 动的路线,经过所 有格子各一次。
解题思路
• 枚举马能走的所有路径, • 直至找到一条完成周游的路径; • 递归,回溯。
• bool solve(int x, int y, int lev) { • route[lev] = x * N + y; • if (lev == M * N - 1) {print_route();return true;} • visited[x][y] = true; • grid grids[8]; • int n=get_grid(grids,x,y); • for (i=0; i<n; i++) • if (solve(grids[i].x, grids[i].y, lev+1)) • return true; • visited[x][y] = false; • return false; • }
优化
• 以上程序速度过慢。 • 优化:改变搜索顺序。
– 先搜索可行格较少的格子。 – 其他顺序。
• 修改get_grid()函数。
• int get_grid(grid grids[], int x,int y) { • int n=0; • for (int i=0; i<8; i++) { • int xx = x + direction[i][0] • int yy = y + direction[i][1]; • if (xx>=0&&yy>=0&&xx<M&&yy<N • &&!visited[xx][yy]) { • grids[n].x = xx; grids[n].y = yy; • grids[n].count = get_count(xx, yy); • n++; • } • } • sort(grids,grids+n); • return n; • }
• bool operator < (const grid &a, const grid &b) { • return a.count < b.count; • } • int get_count(int x, int y) { • int i, xx, yy, count = 0; • for (i=0; i<8; i++) { • xx = x + direction[i][0]; • yy = y + direction[i][1]; • if (xx>=0&&yy>=0&&xx<M&&yy<N&&!visited[xx][y y]) • count++; • } • return count; • }
•
• • •
point operator / (double b){return point(x/b,y/b);}
point operator * (double b){return point(x*b,y*b);} double dis(){return sqrt(x*x+y*y);} void left(){y=-y;swap(x,y);}
1093 Air Express
• 给出4个重量区间以及在每个区间的单位重量运输 价格,再给出一个背包重量,可以往背包里添加 任意多重量,求最低的总运输价格。 • 所有数字不超过1000。 • Package weight Cost per pound • 0 to 9 pounds $10 • 10 to 49 pounds $5 • 50 to 99 pounds $3 • 100 pounds or more $2
1029 Rabbit
• 题目大意: • 开始有一对大兔子。每对大兔子每个月产 生一对小兔子。每对小兔子经过m个月变成 大兔子。问经过d个月后有多少兔子。 • 1<=m<=10, 1<=d<=100
解题思路
• 当m=1时,每个月的兔子数量是上个月的 两倍,经过d=100个月兔子将有2^100个兔 子,超过64位整数表示的范围,因此要用 高精度。 • 每个月的兔子数量,为上一个月的兔子数 量,加上上一个月的大兔子数量,即m个月 前的兔子数量,即a[n]=a[n-1]+a[n-m]。 • 注意当n-m为负数时,也有初始时存大的一 对大兔子。
1206 1012 Stacking Cylinders
• 如图所示,给出最底 层的n个球的位置,求 最顶层的球的位置。 • 1<=n<=10
解题思路
• 关键点在于已知两个圆的圆心坐标,求放 在这两个圆上的圆的圆心坐标。 • 向量+勾股定理。
• struct point • { • double x,y; • • • • point(){x=0;y=0;} point(double a,double b){x=a;y=b;} point operator - (point b){return point(x-b.x,y-b.y);} point operator + (point b){return point(x+b.x,y+b.y);}
解题思路
• n个盘子的汉诺塔问题递归求解:把前n-1 个盘子移到第二根柱子上,把第n个盘子移 到第三根柱子上,把前n-1个盘子移到第三 根柱子上。 • 从一根柱子到另一根柱了,移动1个盘子需 要f(1)=1步,移动k(k>1)个盘子需要f(k)=f(k1)+1+f(k-1)步。 • 即得到f(k)=2^k-1。因此第2^k步移动的是 k+1个盘子。在移动第k+1个盘子后,左右 对移地移动k个盘子
• int get_grid(grid grids[], int x,int y) { • int n=0; • for (int i=0; i<8; i++) { • int xx = x + direction[i][0]; • int yy = y + direction[i][1]; • if(xx>=0&&yy>=0&&xx<M&&yy<N • &&!visited[xx][yy]) { • grids[n].x = xx; • grids[n].y = yy; • n++; • } • } • return n; • }
• 记录子树的大小,删 除的边的两个端点, 子树中的最小编号
• 用vector来保存树的边
不同子树之间的比较
主过程
• 找出树中的最小编号,从该编号开始DFS
1438 Shopaholic
• 买东西每买三件东西则最便宜的一件免费 。给出n个需要买的东西的价格,问最多能 免费多少? • n<=20000,价格不超过20000
• 把p化成二进制数,假设现在p里有超过1个 位为1,设最高位为k,则在2^k步前和2^k 步后对称,因此第p步移的盘子与第p-2^k 步移的盘子一样。 • 直到p里只有1个位为1,设此时p=2^m,则 此步移动的是第m+1个盘子。 • 因此题目转化成二进制数p,从低位起连续 0的位数,加1,为答案。