回溯法-求解最小機器重量設計問題


題目內容:

設某一機器由n個部件組成,部件編號為1~n,每一種部件都可以從m個不同的供應商處購得,供應商編號為1~m。設wij是從供應商j處購得的部件i的重量,cij是相應的價格。對於給定的機器部件重量和機器部件價格,計算總價格不超過d的最小重量機器設計。(注意:輸出結果中第一行最后沒有空格。比如下面的輸出樣例中1 3 1后面沒有空格。)

 

輸入格式:

 

第1行輸入3個正整數n,m和d。接下來n行輸入wij(每行m個整數),最后n行輸入cij(每行m個整數),這里1≤n、m≤100。

 

輸出格式:

 

輸出的第1行包括n個整數,表示每個對應的供應商編號,第2行為對應的最小重量。

 

輸入樣例:

 

3 3 7

1 2 3

3 2 1

2 3 2

1 2 3

5 4 2

2 1 2

 

輸出樣例:

 

1 3 1

4

 

  1 #include<stdio.h>
  2 #define MAXSIZE 150
  3 int main()
  4 {
  5     int n,m,d;
  6     int d_now=0;//當前總價值 
  7     int w_now=0;//當前重量 
  8     int w_min=100;//最小重量 
  9     scanf("%d%d%d",&n,&m,&d);
 10     int w[MAXSIZE][MAXSIZE];
 11     int c[MAXSIZE][MAXSIZE];
 12     for(int i=1;i<=n;i++)
 13     {
 14         for(int j=1;j<=m;j++)
 15         {
 16             scanf("%d",&w[i][j]);
 17         }
 18     }
 19     for(int i=1;i<=n;i++)
 20     {
 21         for(int j=1;j<=m;j++)
 22         {
 23             scanf("%d",&c[i][j]);
 24         }
 25     }
 26     int p[MAXSIZE];//存儲各部件對應供應商編號
 27     int pmin[MAXSIZE];//存儲最小重量對應供應商編號 
 28     for(int i=1;i<=n;i++)
 29         p[i]=0;
 30     int t=1;//
 31     int q=1;//
 32     while(t>0)
 33     {
 34         printf("t = %d , q = %d \n",t,q);
 35 
 36         if(d_now+c[t][q]>d)
 37         {
 38             if(q<m)                //下一個供應商 
 39                 q++;                
 40             else                //上一個部件 
 41             {
 42                 t--;
 43                 d_now-=c[t][p[t]];
 44                 w_now-=w[t][p[t]];
 45                 while(p[t]>=m)
 46                 {
 47                     t--;
 48                     d_now-=c[t][p[t]];
 49                     w_now-=w[t][p[t]];
 50                     if(t<=0)
 51                         break;
 52                 }
 53                 q=p[t]+1;
 54             }            
 55         }
 56         else
 57         {
 58             d_now+=c[t][q];
 59             w_now+=w[t][q];
 60             printf("w_now = %d\n",w_now);
 61             p[t]=q;    
 62             if(t==n)//已到最后一個部件 
 63             {
 64                 if(w_now<w_min)
 65                 {
 66                     for(int i=1;i<=n;i++)
 67                         pmin[i]=p[i];
 68                     w_min=w_now;
 69                     printf("change min!\n");
 70                 }
 71                 if(q<m)                
 72                 {
 73                     w_now-=w[t][q];
 74                     d_now-=c[t][q];
 75                     q++;
 76                 }                
 77                 else
 78                 {
 79 
 80                     d_now-=c[t][p[t]];
 81                     w_now-=w[t][p[t]];    
 82                     t--;                
 83                     printf("t = %d , p[t] = %d\n",t,p[t]);
 84                     printf("w_now = %d\t",w_now);
 85                     d_now-=c[t][p[t]];
 86                     w_now-=w[t][p[t]];
 87                     printf("w_now = %d\n",w_now);
 88                     while(p[t]>=m)
 89                     {
 90                         t--;
 91                         d_now-=c[t][p[t]];
 92                         w_now-=w[t][p[t]];
 93                         if(t<=0)
 94                             break;
 95                     }
 96                     q=p[t]+1;                    
 97                 }            
 98             }
 99             else//未到最后一個部件 
100             {
101                 t++;
102                 q=1;
103             }    
104         }
105         
106     } 
107     for(int i=1;i<n;i++)
108     {
109         printf("%d ",pmin[i]);
110     }
111     printf("%d\n",pmin[n]);
112     printf("%d",w_min);
113     /*    
114     for(int i=0;i<n;i++)
115     {
116         for(int j=0;j<m;j++)
117         {
118             printf("%d",w[i][j]);
119         }
120         printf("\n");
121     }
122     */
123     return 0;
124 }

 

 

題目分析:

  本題意為用n個部件(每個部件有m個供應商用於購買),如何選擇供應商(並滿足價格不超出預算)使得總重量最小。就像我們DIY電腦的時候,如何選擇配件的品牌和型號使得在預算之內組裝一台性能過硬的電腦。回溯的作用主要是為了刷掉超出預算的選擇。因此,在超出預算的情況下,即目前總價格加上當前選擇的供應商價格之和大於預算時:我們需要選擇下一個供應商,如果沒有下一個了,就返回到上一個部件,令上一個部件選擇下一個供應商,以此類推。

        if(d_now+c[t][q]>d)
        {
            if(q<m)                //下一個供應商 
                q++;                
            else                //上一個部件 
            {
                t--;
                d_now-=c[t][p[t]];
                w_now-=w[t][p[t]];
                q=p[t]+1;
            }            
        }

就是這段代碼的含義。

  當然,本程序最重要的還是求出總重量最小的選擇方式,否則再怎么剪枝也沒有任何意義。所以最重要的還是供應商選擇部分代碼。為了遍歷m^n(m,n為變量)個不同的選擇情況,在我們已學知識中是無法用多重for循環來實現的。因此我們用while循環,用t、q來表示當前的所在行、列,因為t、q到達n、m處(即當前訪問到最后一個部件的最后一個供應商)並不能代表完成查詢,因此循環控制條件可以與m,n無關,那么會是什么呢?我們想想,超出預算的時候是怎么做的?當我們沒有下一個供應商了,就得返回上一個部件,當返回到第一個部件時,再就只能返回到第0個(並不存在)了,顯而易見,控制條件可以為while(t>0)。

當t<n時,選擇下一個部件t++,q=1

當t=n&&q<m時,選擇下一個供應商q++

當t=n&&q=m時,返回上一個部件,並為其選擇下一個供應商(這種情況下代碼得用循環實現,因為有可能上一個部件也沒有下一個供應商了)

這種情況下代碼如下:

            d_now-=c[t][p[t]];
                    w_now-=w[t][p[t]];    
                    t--;                
                    printf("t = %d , p[t] = %d\n",t,p[t]);
                    printf("w_now = %d\t",w_now);
                    d_now-=c[t][p[t]];
                    w_now-=w[t][p[t]];
                    printf("w_now = %d\n",w_now);
                    while(p[t]>=m)//當沒有下一個供應商時,不斷返回到上一個部件
                    {
                        t--;
                        d_now-=c[t][p[t]];
                        w_now-=w[t][p[t]];
                        if(t<=0)
                            break;
                    }
                    q=p[t]+1;        

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM