回溯法-求解最小机器重量设计问题

题目内容:

设某一机器由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;        

 

posted @ 2019-04-05 16:45  shadowgully  阅读(7154)  评论(0编辑  收藏  举报