1 / 76

习题选讲

习题选讲. 赵浩泉 wxyz202@soj.me. 动态规划. Pick numbers The Top-Code 过河 The Longest Walk Hie with the Pie 电子眼 Fight Club GECKO 能量项链 Antimonotonicity Missile Bridging Signals 1176 Two ends. 动态规划. Counting Problem HOUSING Circular Sequence Apple Tree Maximum Sum 金明的预算方案 运动会

Download Presentation

习题选讲

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 习题选讲 赵浩泉 wxyz202@soj.me

  2. 动态规划 • Pick numbers • The Top-Code • 过河 • The Longest Walk • Hie with the Pie • 电子眼 • Fight Club • GECKO • 能量项链 • Antimonotonicity • Missile • Bridging Signals • 1176 Two ends

  3. 动态规划 • Counting Problem • HOUSING • Circular Sequence • Apple Tree • Maximum Sum • 金明的预算方案 • 运动会 • Cash Machine • 开心的金明 • Knapsack • Dairy Queen • Hands Shaking • Missile

  4. Pick numbers • 题目大意: • 给一个M*N的格子,现在需要从左上角走到右下角,并且只能往下或往右走,并且要把走过的格子中的数加起来,现在要求和为正并且越小越好,求和。 • 2<=M,N<=10,格子中的数范围为[-10,10]

  5. Pick numbers • 解题思路: • 因为只能往下和往右走,因此走第二行的格子必定在走第一行的格子之后,在同一行里,靠左的格子必定先走。 • 从左上角到右下角总共经过M+N-1个格子,因此得到的和的范围为[-190,190]。

  6. Pick numbers • 使用动态规划,按走的顺序记录到达每个格子可能出现的所有的和。 • 每个格子使用一个数组,如果某个和可能出现,则这个位置标为true。 • 每个格子可能出现的和,只可能从它的左方或上方格子可能出现的和,加上当前格子的值得到。

  7. Pick numbers • mid=200; ans=-1; • dp[1][1][mid+a[1][1]]=true; • for (i=1;i<=m;i++) for (j=1;j<=n;j++) • if (i!=1||j!=1) • for (k=0;k<=mid+mid;k++) { • if (dp[i-1][j][k]) • dp[i][j][k+a[i][j]]=true; • if (dp[i][j-1][k]) • dp[i][j][k+a[i][j]]=true; • } • for (k=mid+1;k<=mid+mid;k++) • if (dp[m][n][k]) { • ans=k-mid; • break; • }

  8. The Top-Code • 题目大意: • 给出一个字符串,要求把它切成长度不小于2的几段,使得每一段的字典序比它前一段的字典序小,求最大能切成的段数,在当前段数下的方案数,在当前段数下字典序最小的方案。 • 字符串长度不超过100。

  9. The Top-Code • 解题思路: • 动态规划。每次要切新的一段,能切出的字符串只决定于上一个切出来的字符串。 • 状态dp[i][j],i表示上一个字符串起始位置,j表示当前字符串起始位置。状态转移时,枚举当前字符串长度,并与上一个字符串长度逐个比较,确定是否能转移。 • 转移过程中更新最多段数,和到达最多段数的方法数,并把所有可行转移边记录下来。 • 寻找字典序最小方案时,从结束状态开始,沿着转移边反向寻找到所有可能经过的状态,再从起始状态开始,只沿可能经过的状态贪心转移,最终到达结束状态。 • http://222.200.182.58/viewsource.php?sid=1095986

  10. 过河 • 题目大意: • 桥的起点为0,终点为L,其中地有M个石子。青蛙每次跳的范围为[S,T],问要跳过桥最小踩到石子次数。 • 1 <= L <= 10^9 • 1 <= S <= T <= 10 • 1 <= M <= 100

  11. 过河 • 解题思路: • L的值大太,直接按L的值进行动态规划不可行。 • 分情况:若S和T相等,则踩到的石子数是固定的; • 若S和T不相等,因为S和T的最大值为10,所以当两个石子相差太远是没有意义的,这里取的值为90,当石子距离相差90以上时,看作90,答案不变。 • 压缩后桥长度不超过10000,直接动态规划即可。

  12. 过河 • for(i=0;i<m;i++) • { • delta[i]=rock[i+1]-rock[i]; • if(delta[i]>90)delta[i]=90; • } • for(i=1;i<=m;i++) • rock[i]=rock[i-1]+delta[i-1]; • for(i=1;i<=m;i++) • dp[rock[i]]=1; • f[0]=1; • work();

  13. 过河 • void work() { • L=rock[m]+90; • for(i=s;i<=L;i++) { • max=101; • for(j=i-t;j<=i-s;j++) • if(j>=0) • if(f[j]&&dp[j]+dp[i]<max) { • max=dp[j]+dp[i]; • f[i]=1; • } • dp[i]=max; • } • }

  14. The Longest Walk • 题目大意: • 给出一个图,求出不经过重复点最长路径。 • 隐藏条件:图的点数不超过12。

  15. The Longest Walk • 解题思路: • 动态规划,状态为已经经过哪些点,当前所在位置。 • 状态转移,在当前状态可以到达哪个没有到达过的点,到达新的状态,并纪录转移。 • 输出路径时,从结束结点开始,按照纪录的转移倒推输出。 • 如果状态转移是倒推的,则输出的结果不需再倒推。

  16. The Longest Walk • for(i=1;i<20;i++) t[i]=t[i-1]*2; • for(i=1;i<t[n];i++)for(j=0;j<n;j++){ • if((i&t[j])==0) continue; • for(k=0;k<n;k++){ • if(j==k||(i&t[k])==0) continue; • if(b[j][k]&&a[j][i]<b[j][k]+a[k][i-t[j]]){ • a[j][i]=b[j][k]+a[k][i-t[j]]; • suc[j][i]=k; • } • } • }

  17. Hie with the Pie • 题目大意: • 给出一个图,求出从起点经过所有点并回到起点的最短路径,可以经过重复点。 • 点数不超过10。

  18. Hie with the Pie • 解题思路: • 动态规划,状态为已经经过哪些点,当前所在位置。 • 状态转移,在当前状态可以到达哪个没有到达过的点,到达新的状态,并纪录转移。 • 因为可以经过重复点,先调用floyd算法求出所有点之间的最短路。

  19. Hie with the Pie • for(int state=opt[0][1<<0]=0;state<(1<<n);state++) • for(i=0;i<n;i++) • if((state&(1<<i))&&opt[i][state]!=-1) • { • for(j=0;j<n;j++) • { • int newstate=state|(1<<j); • if(opt[j][newstate]==-1 • ||opt[j][newstate]>opt[i][state]+g[i][j]) • opt[j][newstate]=opt[i][state]+g[i][j]; • } • } • int ans=opt[0][(1<<n)-1];

  20. 电子眼 • 题目大意: • 一个城市有N条马路和N个路口,在路口安装一个电子眼可以监视和它连接的马路,问最小需要多少电子眼才可以监视所有马路。 • 1<=N<=100000

  21. 电子眼 • 解题思路: • N个结点N-1条边的连通图是树,则N个结点N条边的连通图只存在一个环。 • 假设题目所求的图是树,则可以用树型动态规划解决:从一个结点开始,当前结点包括的状态是,如果子结点连向它的边全部已经被监视需要的电子眼数,和存在还没被监视的边需要的电子眼数,则可先递归算出子结点的信息,在转移时,如果存在边还没被监视,则当前结点必须安装,否则可以安装也可以不安装,取最小值。 • 当图存在一个环的时候,可以找到环中的一条边,分别假设这条边的其中一个结点是否安装电子眼,并分别求解。

  22. 电子眼 • void dfs(long k,long &x1,long &y1,long &x2,long &y2) { • long temp,nextx1,nexty1,nextx2,nexty2; • x1=x2=0; y1=y2=1; • for (node* pp=b[k];pp!=NULL;pp=pp->next) { • temp=pp->xx; • if ((temp==p&&k==q)||(temp==q&&k==p)); • else if (d[temp]==0) { • d[temp]=1; • dfs(temp,nextx1,nexty1,nextx2,nexty2); • x1+=nexty1; x2+=nexty2; • if (nextx1<nexty1) y1+=nextx1; • else y1+=nexty1; • if (nextx2<nexty2) y2+=nextx2; • else y2+=nexty2; • } • } • if (k==q) x1=y2=1000000; • }

  23. Fight Club • 题目大意: • N个人站成一圈,每个人都只能与相邻的人战斗,输的人离开这个圈,直到圈里只剩下一个人。给出每两个人之间的战斗胜负关系表,求出每个人是否能成为圈里剩下的唯一一个人。

  24. Fight Club • 解题思路: • 要成为圈中唯一剩下的人,则其他人全部都要打败,而每个人只能与旁边的人战斗,因此对于每个状态下每个人只关心两边的人。 • 动态规划,状态为区间[i,j],表示从第i个人逆时针方向数到第j个人之间所有人(不包括i和j)都已经被打败。 • 状态转移,每个区间[i,j]能够达到,则需要在i和j之间存在一个人k,使得[i,k]能够成立,且[k,j]能够成立,且k会被i和j其中一个人打败。 • 初始时长度为0的区间必定成立,且长度较大的区间需要长度较小的区间得到,因此枚举顺序为区间的长度从小到大。 • 最后对于每个人i,如果[i,i]成立,则表示所有其它人都被打败。

  25. Fight Club • memset(f, 0, sizeof(f)); • for (int i=0; i<n; i++) • f[i][(i+1)%n] = 1; • for (int d=2; d<=n; d++) • for (int i=0; i<n; i++) { • int j = (i+d)%n; • for (int k=(i+1)%n; k!=j; k=(k+1)%n) • if (f[i][k] && f[k][j] && (a[i][k] || a[j][k])) { • f[i][j] = 1; • break; • } • }

  26. GECKO • 题目大意: • 一只壁虎在墙上的格子爬,每个格子上有不同数量的蚊子。壁虎只能从第一行格子开始,每一次爬动都只能爬到下一行格子,只能爬到它所在格子的下一行位置或旁边位置的格子。它每到达一个格子就把格子中的蚊子吃了。问最多能吃的蚊子数量。 • 格子的行数和列数不超过500。

  27. GECKO • 解题思路: • 动态规划,状态为一个格子,如果壁虎现在在这个格子里,它最多已经吃了的蚊子数量。 • 状态转移,对于每一个格子,都只能从上一行的三个格子转移过来,取其中的最大值即可。 • 按行的顺序枚举。

  28. GECKO • for (i=1;i<h;i++) • { • a[i][0]+=max(a[i-1][0],a[i-1][1]); • for (j=1;j<w-1;j++) • a[i][j]+=max(a[i-1][j-1],a[i-1][j],a[i-1][j+1]); • a[i][w-1]+=max(a[i-1][w-1],a[i-1][w-2]); • }

  29. 能量项链 • 题目大意: • 给出一串项链,每次可以选相邻两个珠子进行聚合,释放出一定的能量,并产生一个新珠子。项链是头尾相接的。求释放的能量的总和的最大值。 • 项链长度不超过100。

  30. 能量项链 • 解题思路: • 每次聚合,都会使数字中一的个数字消失。 • 动态规划,状态为[i,j]表示从i开始,按顺时针方向到j,这一段珠子所聚合得到的能量最大值。 • 状态转移,要求出[i,j]的值,则存在一个k在i和j之间,[i,j]的值为[i,k]的值与[k+1,j]的值与这次聚合所释放出的能量的总和,取最大值。 • 且长度较大的区间需要长度较小的区间得到,因此枚举顺序为区间的长度从小到大。

  31. 能量项链 • for (step=1;step<n;step++) • for (i=0;i<n*2;i++) { • j=i+step; • if (j>=n*2) break; • for (k=i;k<j;k++) • temp=ans[i][k]+ans[k+1][j]+a[i]*a[k+1]*a[j+1]; • if (ans[i][j]<temp) • ans[i][j]=temp; • }

  32. Antimonotonicity Missile • 题目大意: • 给出一列数,现在要找出最长的子序列,满足第一个数大于第二个数,第二数小于第三个数,第三个数大于第四个数,如此类推。

  33. Antimonotonicity Missile • 解题思路: • 动态规划,状态为在当前的数字,最长的子序列长度。 • 状态转移,若在原数列中,当前数字比上一个数字小,若子序列长度为奇数,则可加上当前数字,若子序列长度为偶数,则可用当前数字替换上一个数字,长度不变;若当前数字比上一个数字大,也可同理。

  34. Antimonotonicity Missile • l=1; • for (i=2;i<=n;i++) • { • if (a[i]<a[i-1]&&l%2==1) • l++; • else if (a[i]>a[i-1]&&l%2==0) • l++; • }

  35. Bridging Signals • 题目大意: • 左边有一列p个数,右边有一列p个数,并给出它们之间的连线情况,问最多可以保留多少个连线,使到中间的连线不会出现交叉。 • P<40000

  36. Bridging Signals • 解题思路: • 要求最大的不交叉连线数量,问题等价于求最长上升子序列。 • 最长上升子序列动态规划解法: • 状态,以当前数字为子序列的最后一个数,则子序列的最大长度; • 状态转移,对每个数字,找出在这个数字之前出现并且比它小的所有数字,并且从其中找出长度最大的数,加上一即是当前数字的最大子串长度。 • D[i]=max{1,D[j]+1} j=1,2,...,i-1并且 A[i]>A[j] • 复杂度O(N^2) • 通过以上解法可以改进成O(NlogN)的。按数字大小建立线段树,那么在线段树上的操作只有插入一个数和查找某个区间的最大值,可以实现。

  37. Bridging Signals • 最长上升子序列的其它解法: • 维护一个一维数组,数组里的第i个数表示最长上升子序列长度是i的所有子序列中末尾最小的那个数。因此这个数组是递增的。按顺序插入每一个数的时候,在数组中找到比它大的最小的数,并更新这个位置;如果它比所有数都大,增数组长度增加一,并把它放在最末端。寻找的时候使用二分查找。 • 最后的最大长度等于数组长度。复杂度O(NlogN) • 例如,原序列为1,5,8,3,6,7 • 数组为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终数组为1,3,6,7。最长递增子序列为长度4。

  38. Bridging Signals • nn=0;c[0]=0; • for (i=1;i<=n;i++) { • if (a[i]>c[nn]) { • c[++nn]=a[i]; • b[i]=nn; • } • else { • hi=upper_bound(c,c+nn+1,a[i])-c; • c[hi]=a[i]; • b[i]=hi; • } • }

  39. Counting Problem • 题目大意: • 一个N*N的棋盘,在里面放棋子,使得每行有且只有两个棋子,每列有且只有两个棋子,问有多少种方法。通过交换两行或交换两列的方法得到相同的棋盘看作同一种。 • N<=500

  40. Counting Problem • 解题思路: • 问题可转化为把N分解成大于2的整数之和有多少种分法。 • 动态规划,状态a[i][j]表示把i分解成大于j的整数之和有多少种分法。 • 状态转移,a[i][j]=sum(a[i-k][k])(k>=j)。

  41. Counting Problem • for (i=0;i<=500;i++) { • for (j=0;j<=500;j++) { • if (i==0) • a[i][j]=1; • else if (i<j) • a[i][j]=0; • else { • a[i][j]=0; • for (k=max(2,j);k<=i;k++) • a[i][j]=(a[i][j]+a[i-k][k])%1000007; • } • } • ans[i]=a[i][2]; • }

  42. HOUSING • 题目大意: • N个人分到屋里,每间房至少5个人,有多少种分法? • 5 <= n <= 100

  43. HOUSING • 解题思路: • 问题可转化为把N分解成大于5的整数之和有多少种分法。 • 和上题一样。

  44. HOUSING • for (i=0;i<=100;i++) { • for (j=0;j<=100;j++) { • if (i==0) • a[i][j]=1; • else if (i<j) • a[i][j]=0; • else { • a[i][j]=0; • for (k=max(5,j);k<=i;k++) • a[i][j]=a[i][j]+a[i-k][k]; • } • } • ans[i]=a[i][5]; • }

  45. Circular Sequence • 题目大意: • 有一个长度为n的循环队列,求出一个和最大的连续子串。 • 1<=n<=100000

  46. Circular Sequence • 解题思路: • 首先判断序列中是否全部都是负数,如果是的话,最大和即为最大的负数。 • 如果序列并不是循环的,有已知的O(n)方法求出最大值。 • int cal() { • int answer=0,sum=0; • for (int i=0;i<n;i++) { • sum+=i; • if (sum>ans) ans=sum; • if (sum<0) sum=0; • }

  47. Circular Sequence • 则求循环队列的最大值,可以当作不循环队列,可能是最大和,也可能是总和减去最小和。 • sum1=cal(); • for (i=0;i<n;i++) { • sum+=a[i]; • a[i]=-a[i]; • } • sum2=sum+cal(); • return max(sum1,sum2);

  48. Apple Tree • 题目大意: • 有一棵苹果树有N个结点,每个结点上有苹果,最多可以沿着苹果树的边走K步,每走过一个结点把上面的苹果吃掉。问最多可以吃多少个苹果。 • 1<=N<=100, 0<=K<=200

  49. Apple Tree • 解题思路: • 使用树型动态规划,每个结点的状态为,以这个结点为根的子树上使用不同步数,并且是否会回来原结点,所吃的苹果的最大值。 • 状态转移,每个结点,对于每个子结点,可以枚举分配多少步数到这个子结点上。 • http://222.200.182.58/viewsource.php?sid=102025

  50. Maximum Sum • 题目大意: • 有一个数列,求出两个连续的子序列,使得它们的和最大。

More Related