問題描述:
設某一機器由n個部件組成,每一種部件都可以從m個不同的供應商處購得。設是從供應商j處購得的部件i的重量,是相應的價格。試設計一個優先隊列式分支限界法,給出總價格不超過d的最小重量機器設計。
[之所以想記錄這個問題,是因為我覺得自己"用各個部件的最小重量作為未來最理想重量"的這個設計還挺特別。其他都是實驗報告中的內容]
算法描述:
算法實現:
#include<stdio.h> #include<stdlib.h> struct NodeType { int *mark; //記錄部件i的供應商 int preview; //當前已買部件重量+未來最理想重量 int now; //當前考慮買的部件 int nowd; //當前已買部件價格 struct NodeType* next; }; #define MAXINT 65534; int doit(int n,int m,int d,int **w,int **c,int *future) { int frist=0; while(frist<m&&c[0][frist]>d) frist++; //找第一個部件的不超過價格上限的供應商frist if(frist==m)return 0; //第一個部件的所有供應商的價格都超過價格上限,無法購買 /* 構造優先隊列的第一個結點 */ struct NodeType *h,*t,*temp; h=t=temp=temp=(struct NodeType *)malloc(sizeof(struct NodeType)); temp->mark=(int *)malloc((sizeof(int))*n); temp->preview=future[0]+w[0][frist]; //當前已買部件重量+未來最理想重量 temp->now=0; //當前考慮買的部件為部件0 temp->nowd=c[0][frist]; //當前已買部件價格(部件0在供應商frist處購買的價格) temp->mark[0]=frist; //部件0在供應商frist處購買 temp->next=NULL; for(int i=frist+1;i<m;i++) { if(c[0][i]<=d) //若在供應商i處購買部件0的價格沒有超過價格上限(約束剪枝) { temp=(struct NodeType *)malloc(sizeof(struct NodeType)); temp->mark=(int *)malloc((sizeof(int))*n); temp->preview=future[0]+w[0][i];//當前已買部件重量+未來最理想重量 temp->now=0; //當前考慮買的部件為部件0 temp->nowd=c[0][i]; //當前已買部件價格(部件0在供應商i處購買的價格) temp->mark[0]=i; //部件0在供應商i處購買 temp->next=NULL; t->next=temp; t=t->next; } } int realmin=MAXINT; //記錄最小重量 int *markmin; markmin=(int *)malloc((sizeof(int))*n); //記錄部件i的供應商markmin[i] struct NodeType *minr,*minp,*r,*q; while(h!=NULL) { /* 找到隊列中重量最小的結點作為擴展結點(minp) */ int min=realmin;//- r=q=h; while(q!=NULL) { if(q->preview<min) { min=q->preview; minr=r; minp=q; } r=q; q=q->next; } for(int i=0;i<m;i++) { if(minp->nowd+c[minp->now+1][i]<=d) //若已買部件價格加上在供應商i處購買 //部件minp->now+1的價格小於等於價格上限 { temp=(struct NodeType *)malloc(sizeof(struct NodeType)); temp->mark=(int *)malloc((sizeof(int))*n); /* 當前已買部件重量+未來最理想重量 */ temp->preview=0; for(int j=0;j<(minp->now+1);j++) temp->preview+=w[j][minp->mark[j]]; //先計入之前已買的部件的重量 temp->preview+=w[minp->now+1][i]; //在計入當期在供應商i處購買部件minp->now+1的重量 temp->preview+=future[minp->now+1]; //最后計入未來最理想的重量 temp->now=minp->now+1;
//當前考慮買的部件為部件minp->now+1 temp->nowd=minp->nowd+c[minp->now+1][i]; //當前之前已買部件價格+在供應商i處購買部件 //minp->now+1的價格 /* 記錄之前已買部件的供應商+當前部件的供應商i */ for(int j=0;j<(minp->now+1);j++) temp->mark[j]=minp->mark[j]; temp->mark[minp->now+1]=i; temp->next=NULL; if(temp->now+1==n&&temp->preview<realmin) //若當前考慮的部件已經是最后一個部件, //且在供應商i處購買這個部件, //能夠使得現在部件總重量優於當前求解出的最優值 { /*記錄最優值和最優解*/ realmin=temp->preview; for(int j=0;j<n;j++)markmin[j]=temp->mark[j]; free(temp); } else { //若當前考慮的部件並非最后一個部件,將此結點加入隊列 t->next=temp; t=t->next; } } } if(minp==h)//拓展結點為頭結點 { h=h->next; free(minp);//將拓展結點釋放 } else //拓展結點非頭結點 { minr->next=minp->next; free(minp);//將拓展結點釋放 } /* 如果當前隊列中的結點的最理想重量大於已經求出的最優解,則將該結點釋放 */ while(h->preview>=realmin&&h!=t) { q=h; h=h->next; free(q); } if(h->preview>=realmin) { free(h); h=NULL; } } /* 輸出最優值及最優解 */ printf("%d\n",realmin); for(int i=0;i<n;i++)printf("%d ",markmin[i]+1); return 0; } int main() { int n,m,d; scanf("%d %d %d",&n,&m,&d);//部件數,供應商數,價格上限 int ** c; c=(int **)malloc((sizeof(int *))*n); for(int i=0;i<m;i++) c[i]=(int *)malloc((sizeof(int))*m); for(int i=0;i<n;i++) for(int j=0;j<m;j++) scanf("%d",&c[i][j]); //在供應商j處購買部件i的價格c[i][j] int ** w; w=(int **)malloc((sizeof(int *))*n); for(int i=0;i<m;i++) w[i]=(int *)malloc((sizeof(int))*m); for(int i=0;i<n;i++) for(int j=0;j<m;j++) scanf("%d",&w[i][j]); //在供應商j處購買部件i的重量w[i][j] int * future; future=(int *)malloc((sizeof(int))*n); for(int i=0;i<n;i++) { int min=MAXINT; for(int j=0;j<m;j++ ) if(min>w[i][j]) min=w[i][j]; future[i]=min; } for(int i=0;i<n-1;i++) { future[i]=0; for(int j=i+1;j<n;j++) future[i]+=future[j]; } future[n-1]=0; //購買完部件i后剩余部件的最理想重量 doit(n,m,d,w,c,future); //優先隊列分支限界算法 return 0; }