背包问题,dp解决。
dp[i][j][k][m][n]表示第0-4种物品分别购买i-n个时,所需的最小费用。
那么对于每一个offer
dp[i][j][k][m][n] = min ( dp[i][j][k][m][n], dp[i-o[0]][j-o[1]][k-o[2]][m-o[3]][n-o[4]]+offer.cost);
analysis中提出用最短路径的方法来解,思路很巧妙。把”装有不同种数和数量的物品“的篮子看作结点,把offer作为边(把购买一件物品的原始价格看成一种退化的offer),把价格作为边长度。这样就转换成从篮子(0,0,0,0,0)到所求结点的最短路径问题。
代码如下:
#include <iostream>
#include <fstream>
using namespace std;
ifstream fin("shopping.in");
ofstream fout("shopping.out");
#ifdef _DEBUG
#define out cout
#define in cin
#else
#define out fout
#define in fin
#endif
int dp[6][6][6][6][6];
struct offer{
    int pro_num;
    int price;
    int product[5];
    int amount[5];
};
int offer_num;
offer offers[100];
int map[1000]; //product id到”按物品出现顺序所给的编号“的映射
int price[1000];//product id对应的物品价格
int product_num;//物品总数目
int products[5];//存放product id
int amount[5];//product 所需的数量
void solve()
{
    in>>offer_num;
    for(int i=0;i<offer_num;++i){
        in>>offers[i].pro_num;
        for(int j=0;j<offers[i].pro_num;++j){
            in>>offers[i].product[j]>>offers[i].amount[j];
        }
        in>>offers[i].price;
    }
    
    int pro_idx = 0;
    in>>product_num;
    for(int i=0;i<product_num;++i){
        in>>products[i];
        in>>amount[i];
        in>>price[i];
        map[ products[i] ] = i;
    }
    //没有折扣时的价格
    for(int i=0;i<=5;++i)
        for(int j=0;j<=5;++j)
            for(int k=0;k<=5;++k)
                for(int m=0;m<=5;++m)
                    for(int n=0;n<=5;++n){
                        dp[i][j][k][m][n] = 
                            price[0]*i+
                            price[1]*j+
                            price[2]*k+
                            price[3]*m+
                            price[4]*n;
                    }
    for(int i=0;i<=5;++i)
        for(int j=0;j<=5;++j)
            for(int k=0;k<=5;++k)
                for(int m=0;m<=5;++m)
                    for(int n=0;n<=5;++n){
                        int tmp[5];
                        for(int s=0;s<offer_num;++s){
                            memset(tmp,0,sizeof(tmp));    
                            for(int t=0;t<offers[s].pro_num;++t){
                                tmp[map[offers[s].product[t]]] = offers[s].amount[t];
                            }
                            if(i>=tmp[0]&&j>=tmp[1]&&k>=tmp[2]&&m>=tmp[3]&&n>=tmp[4]){
                                dp[i][j][k][m][n] = min( dp[i][j][k][m][n],
                                        dp[i-tmp[0]][j-tmp[1]][k-tmp[2]][m-tmp[3]][n-tmp[4]]+
                                        offers[s].price);
                            }
                        }
                    }
    out<<dp[amount[0]][amount[1]][amount[2]][amount[3]][amount[4]]<<endl;
}
int main(int argc,char *argv[])
{
    solve(); 
    return 0;
}
上面代码有个严重的bug,谢谢网友“我也凑热闹"指出。由于map所有值都为0。所以未在商品列表中出现的商品的map值都为0,即都映射为第一个商品。现改成将map初始化为-1,并增加判断语句。此外将初始化dp的语句合并到后面,以简化代码。
#include <iostream>
#include <fstream>
using namespace std;
ifstream fin("shopping.in");
ofstream fout("shopping.out");
#ifdef _DEBUG
#define out cout
#define in cin
#else
#define out fout
#define in fin
#endif
int dp[6][6][6][6][6];
struct offer{
    int pro_num;
    int price;
    int product[5];
    int amount[5];
};
int offer_num;
offer offers[100];
int map[1000];
int price[1000];
int product_num;
int products[5];
int amount[5];
void solve()
{
    in>>offer_num;
    for(int i=0;i<offer_num;++i){
        in>>offers[i].pro_num;
        for(int j=0;j<offers[i].pro_num;++j){
            in>>offers[i].product[j]>>offers[i].amount[j];
        }
        in>>offers[i].price;
    }
    
    int pro_idx = 0;
    in>>product_num;
    //2009.07.27修改
    memset(map,-1,sizeof(map));
    for(int i=0;i<product_num;++i){
        in>>products[i];
        in>>amount[i];
        in>>price[i];
        map[ products[i] ] = i;
    }
    for(int i=0;i<=5;++i)
        for(int j=0;j<=5;++j)
            for(int k=0;k<=5;++k)
                for(int m=0;m<=5;++m)
                    for(int n=0;n<=5;++n){
                         dp[i][j][k][m][n] = 
                            price[0]*i+
                            price[1]*j+
                            price[2]*k+
                            price[3]*m+
                            price[4]*n;
                        int tmp[5];
                        for(int s=0;s<offer_num;++s){
                            memset(tmp,0,sizeof(tmp));    
                            for(int t=0;t<offers[s].pro_num;++t){
                                //2009.07.27修改
                                if( map[offers[s].product[t]]!=-1)
                                    tmp[map[offers[s].product[t]]] = offers[s].amount[t];
                            }
                            if(i>=tmp[0]&&j>=tmp[1]&&k>=tmp[2]&&m>=tmp[3]&&n>=tmp[4]){
                                dp[i][j][k][m][n] = min( dp[i][j][k][m][n],
                                        dp[i-tmp[0]][j-tmp[1]][k-tmp[2]][m-tmp[3]][n-tmp[4]]+
                                        offers[s].price);
                            }
                        }
                    }
    out<<dp[amount[0]][amount[1]][amount[2]][amount[3]][amount[4]]<<endl;
}
int main(int argc,char *argv[])
{
    solve(); 
    return 0;
}