酷暑一夏1

不忘初心,方得始终


USACO Chapter3 Section3 Prob 2 Solution

一、翻译(来源

在商店中,每一种商品都有一个价格(用整数表示)。例如,一朵花的价格是 2 zorkmids (z),而一个花瓶的价格是 5z 。为了吸引更多的顾客,商店举行了促销活动。
促销活动把一个或多个商品组合起来降价销售,例如:
三朵花的价格是 5z 而不是 6z, 两个花瓶和一朵花的价格是 10z 而不是 12z。 编写一个程序,计算顾客购买一定商品的花费,尽量利用优惠使花费最少。尽管有时候添加其他商品可以获得更少的花费,但是你不能这么做。
对于上面的商品信息,购买三朵花和两个花瓶的最少花费的方案是:以优惠价购买两个花瓶和一朵花(10z),以原价购买两朵花(4z)。
INPUT FORMAT:(file shopping.in)
输入文件包括一些商店提供的优惠信息,接着是购物清单。(最多有5种商品)
第一行 优惠方案的种类数(0 <= s <= 99)。
第二行..第s+1 行 每一行都用几个整数来表示一种优惠方式。第一个整数 n (1 <= n <= 5),表示这种优惠方式由 n 种商品组成。后面 n 对整数 c 和 k 表示 k (1 <= k <= 5)个编号为 c (1 <= c <= 999)的商品共同构成这种优惠,最后的整数 p 表示这种优惠的优惠价(1 <= p <= 9999)。优惠价总是比原价低。
第 s+2 行 这一行有一个整数 b (0 <= b <= 5),表示需要购买 b 种不同的商品。
第 s+3 行..第 s+b+2 行 这 b 行中的每一行包括三个整数:c,k,p。 c 表示唯一的商品编号(1 <= c <= 999),k 表示需要购买的 c 商品的数量(1 <= k <= 5)。p 表示 c 商品的原价(1 <= p <= 999)。最多购买 5*5=25 个商品。
OUTPUT FORMAT:(file shopping.out)
只有一行,输出一个整数:购买这些物品的最低价格。

二、题解

多重背包问题,对于编号问题要好好处理。

对输入的每个编号进行重排,使得编号范围在$[1,5]$内。这里用map来达成目的。
先初始化,设买$c_j$个物品$j$,编号为$i$的物品价格为(公式1) 。($0\le c_1, c_2, c_3, c_4, c_5\le 5, 1 \le i \le 5$)
然后通过01背包转化,此处背包大小为无穷大。
设使用方案$i$,$c_j$仍然为上面的意思,$price[i][j]$表示方案$i$要买物品$j$的个数(特别地,(公式2)表示优惠价)。此时在满足(公式4)的情况下,(公式3)($0\le c_1, c_2, c_3, c_4, c_5\le 5, 1 \le i \le n$)
最后$dp[num_1][num_2][num_3][num_4][num_5]$为所求。$num_k$表示购买物品$k$的个数。

引用

公式1:


公式2:

公式3:

公式4:

公式1、2、3、4引用自codecogs因为实在太长辣(:зゝ∠)

三、代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/*
ID:
PROB: shopping
LANG: C++
*/
#include<cstdio>
#include<map>
#include<algorithm>
std::map<int,int>map;
const int MAXN=5+5;
const int MAXB=5+5;
const int MAXT=99+5;
int p[MAXT][MAXN],dp[MAXB][MAXB][MAXB][MAXB][MAXB],pr[MAXN],recylebin,cnt,num[MAXN];
int findmap(int x)
{
int m=map[x];
if(!m)
{
map[x]=++cnt;
return cnt;
}
else return m;
}
int main()
{
int n,a,b;
freopen("shopping.in","r",stdin);
freopen("shopping.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
for(int j=1;j<=a;j++)
{
scanf("%d",&b);
b=findmap(b);
scanf("%d",&p[i][b]);
}
scanf("%d",&p[i][0]);
}
scanf("%d",&a);
for(int i=1;i<=a;i++)
{
scanf("%d",&b);
b=findmap(b);
scanf("%d",&num[b]);
scanf("%d",&pr[b]);
}
for(int i=0;i<=num[1];i++)
for(int j=0;j<=num[2];j++)
for(int k=0;k<=num[3];k++)
for(int l=0;l<=num[4];l++)
for(int m=0;m<=num[5];m++)
dp[i][j][k][l][m]=i*pr[1]+j*pr[2]+k*pr[3]+l*pr[4]+m*pr[5];
for(int now=1;now<=n;now++)
for(int i=0;i<=num[1];i++)
for(int j=0;j<=num[2];j++)
for(int k=0;k<=num[3];k++)
for(int l=0;l<=num[4];l++)
for(int m=0;m<=num[5];m++)
if(i-p[now][1]>=0 && j-p[now][2]>=0 && k-p[now][3]>=0 && l-p[now][4]>=0 && m-p[now][5]>=0)
dp[i][j][k][l][m]=std::min(dp[i][j][k][l][m],dp[i-p[now][1]][j-p[now][2]][k-p[now][3]][l-p[now][4]][m-p[now][5]]+p[now][0]);
printf("%d\n",dp[num[1]][num[2]][num[3]][num[4]][num[5]]);
return 0;
}
最近的文章

USACO 2017 February Contest, Silver

ResultThe name and year was hidden.12345* = Correctx = Wrong Answer (including possibly empty or missing output file)Country|Year|Name |Score |helpcross |maxcross |countcrossCHN |····|··········|833 |* x * * x x * x x *|* * * * * * * * * * *|* * * * * * * * * * Solution …

于  USACO, 比赛 继续阅读
更早的文章

USACO Chapter3 Section3 Prob 1 Solution

一、翻译(来源)Farmer John每年有很多栅栏要修理。他总是骑着马穿过每一个栅栏并修复它破损的地方。John是一个与其他农民一样懒的人。他讨厌骑马,因此从来不两次经过一个栅栏。你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点)。一个顶点上可连接任意多(&gt;=1)个栅栏。两顶点间可能有多个栅栏。所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一位较小的,如果还有多组解,输出第二位较小的,等等)。输入数据保证至少有一个解。 二、题解 …

于  USACO, USACO Training 继续阅读