2

My question basically a modified of a question of this link Modified Tower of Hanoi

In that question, it assumes that some of the discs have same size. My question is a little more complicated. Let's assume those discs with same size having different color. And when you move those discs from a to c, you have to make sure that the discs should be in the same order as it was placed at the beginning. We need to find the least moves of this problem.

I have an example here.

modified tower of hanoi

As you can see, if we have 1 disc whose size is 1 and 2 discs whose size are 2. We at least need to move 7 times.

Thx for Kenny Ostrom. Now firstly, I know if you have multiple same sized plates on the bottom, then you must take the one bottom plate and pretend it is larger than the others, even if it is the same size. And secondly, I think for a group of discs of same size which is in the middle, every disc except the bottom one should be moved twice because we try to have a right order.

However, it does not seem to lead to the right answer.

It is a question of our c++ homework and the questions gives:

N represents how many size of disc do we have, which is between 1 and 15000

M is just a number, which is between 1 and 1000000.

a[i] represents the number of the disc whose size is i

and I need to print out (the least number of the moves) mod M. So i am guessing maybe there should be a better way to calculate (2^n) mod M?

Here is my code:

#include<iostream>
#include <math.h>
using namespace std;
int main()
{
    int N,M; //for an example:N=2,M=1000
    cin>>N>>M;
    int *a=new int[N+2];
    for(int i=1;i<=N;i++) cin>>a[i]; //I abandon the first element of this array
                                     // just to make it easier for after
                                     // for an example: {0,1,2}

    if(a[N]>1){      //take the bottom one as a seperate group
        a[N]=a[N]-1;
        a[N+1]=1;
    }
    int j=N+1;

    int num=0;

    for(int i=1;i<=j;i++){
        if(a[i]<=1){
            num=num+pow(2,j-i);
        }
        else{
            num=num+pow(2,j-i)*(2*(a[i]-1)+1);//for every group of discs in the middle
        }
    }
    cout<<num;
    cout<<num%M;

    return 0;
}

Any suggestions or pointers are welcome. Thanks!

Community
  • 1
  • 1
Penguin Zhou
  • 131
  • 2
  • 10
  • pow(2, x) could be done with bit shifting, as 1 << x, assuming all positive integers and no overflow. – Kenny Ostrom Oct 22 '16 at 16:13
  • The first element of the array "a" with N elements is a[0]. The last element is a[N-1] – Kenny Ostrom Oct 23 '16 at 13:37
  • int M serves no purpose other than to (sometimes) make the last printed number different for no reason? – Kenny Ostrom Oct 23 '16 at 13:38
  • Oh yeah, 7 was correct for the example you gave. How would you represent that example in your array? {1,2}? So you are accounting for the decrease in tower size, and a[i] represents the number of same size plates at level i? – Kenny Ostrom Oct 23 '16 at 13:53
  • So you are saying "if there are multiple plates" then do this separate computation with *a[i] to account for moving multiple plates. But you also have an extra * 2, so you move them twice (to preserver their order)? But this is wrong. You are only guaranteed to have one peg to move them to, so you can't do that. It is also wrong because it is sub-optimal. You only need to preserve order in the final state. – Kenny Ostrom Oct 23 '16 at 14:00
  • I see. It is a sub-optimal answer. So maybe this problem can only be solved in a recursive way? – Penguin Zhou Oct 23 '16 at 15:37

1 Answers1

1

The key insight of the "same size disks" solution you linked to is that the optimal strategy will always be to move same sized disks as a unit, so you condense same size disks into one disk with increased cost. Same sized disks do not count for the height of the tower, but they do count for the cost of actually moving them as a group.

But if the final answer must retain the original order, then your new constraint is that you must move the same sized plates an even number of times. This is because you move them by taking the top one first, and so on, until you move the bottom one on top of all the other same sized plates. Thus they are reversed by moving once (or any odd number of times), but if you do that twice (or any even number of times) then they will be reversed again and therefore in the original order.

However, reviewing the posted solution for moving same sized plates, you see that each group will always move a power of 2 times, from 2^(n-1) at the top, all the way down to 2^0 at the bottom. But that is always an even number except for the bottom. Therefore, if you have multiple same sized plates on the bottom, then you must take the one bottom plate and pretend it is larger than the others, even if it is the same size.

Since this is homework, I will not be posting code, but it should be easy once you figure out what's going on. It was mostly covered in the answer you linked to, with the only change being that a "same sized plates" group must move an even number of times in order to be in the original stacking order.

Kenny Ostrom
  • 5,639
  • 2
  • 21
  • 30
  • Thank you for this. I wrote this code as what you said. It gives the correct answer for the example question. However, seems it still has some mistake because I could not get the right answer. – Penguin Zhou Oct 23 '16 at 09:24
  • #include #include using namespace std; int main() { int N,M; cin>>N>>M; int *a=new int[N+2]; for(int i=1;i<=N;i++) cin>>a[i]; int num=0; if (a[N]==1){ for(int i=1;i<=N;i++){ if(a[i]<=1){ num=num+pow(2,N-i); } else{ num=num+pow(2,N-i)*2*a[i]; } } } else{ a[N]=a[N]-1; a[N+1]=1; for(int i=1;i<=N+1;i++){ if(a[i]<=1){ num=num+pow(2,N+1-i); } else{ num=num+pow(2,N+1-i)*2*a[i]; } } } cout< – Penguin Zhou Oct 23 '16 at 09:26
  • #include #include using namespace std; int main() { int N,M; cin>>N>>M; int *a=new int[N+2]; for(int i=1;i<=N;i++) cin>>a[i]; int num=0; if (a[N]==1){ for(int i=1;i<=N;i++){ if(a[i]<=1){ num=num+pow(2,N-i); } else{ num=num+pow(2,N-i)*2*a[i]; } } } else{ a[N]=a[N]-1; a[N+1]=1; for(int i=1;i<=N+1;i++){ if(a[i]<=1){ num=num+pow(2,N+1-i); } else{ num=num+pow(2,N+1-i)*2*a[i]; } } } cout< – Penguin Zhou Oct 23 '16 at 09:30
  • #include #include using namespace std; int main() { int N,M; cin>>N>>M; int *a=new int[N+2]; for(int i=1;i<=N;i++) cin>>a[i]; int num=0; if (a[N]==1){ for(int i=1;i<=N;i++){ if(a[i]<=1){ num=num+pow(2,N-i); } else{ num=num+pow(2,N-i)*2*a[i]; } } } else{ a[N]=a[N]-1; a[N+1]=1; for(int i=1;i<=N+1;i++){ if(a[i]<=1){ num=num+pow(2,N+1-i); } else{ num=num+pow(2,N+1-i)*2*a[i]; } } } cout< – Penguin Zhou Oct 23 '16 at 09:31
  • I am sorry. I am new here and somehow I do not understand how it works. I am wondering if it is an algorithm problem or maybe just my code or the num is too big for the module. – Penguin Zhou Oct 23 '16 at 09:34
  • Understanding the algorithm is just the first step, but it is the first step. If you want to post code, you can "edit" your question and correct it there. I would start over after re-reading the modified tower of hanoi. Also don't do user input, use std::vector tower = {1, 2, 2}; Make sure it handles known test cases correctly before you start taking user input. – Kenny Ostrom Oct 23 '16 at 13:33
  • Get you! Thank u so much! – Penguin Zhou Oct 23 '16 at 15:38