题面
Bob came to a cash & carry store, put n items into his trolley, and went to the checkout counter to pay. Each item is described by its price ciand time ti in seconds that a checkout assistant spends on this item. While the checkout assistant is occupied with some item, Bob can steal some other items from his trolley. To steal one item Bob needs exactly 1 second. What is the minimum amount of money that Bob will have to pay to the checkout assistant? Remember, please, that it is Bob, who determines the order of items for the checkout assistant.
The first input line contains number n (1 ≤ n ≤ 2000). In each of the following n lines each item is described by a pair of numbers ti, ci(0 ≤ ti ≤ 2000, 1 ≤ ci ≤ 109). If ti is 0, Bob won't be able to steal anything, while the checkout assistant is occupied with item i.
Output one number — answer to the problem: what is the minimum amount of money that Bob will have to pay.
4
2 10
0 20
1 5
1 3
8
3
0 1
0 10
0 100
111
题目大意
Bob 来到一家现购自运商店,将 n 件商品放入了他的手推车,然后到收银台 付款。每件商品由它的价格 pi 和收银员扫描它的时间 ti 秒定义。当收银员正在扫 描某件商品时,Bob 可以从他的手推车中偷走某些其它商品。Bob 需要恰好 1 秒 来偷走一件商品。Bob 需要付给收银员的最少钱数是多少?请记住,收银员扫描 商品的顺序由 Bob 决定。 输入输出格式 输入格式: 输入第一行包含数 n(1≤n≤2000)。接下来 n 行每行每件商品由 一对数 ti,ci(0≤ti≤2000,1≤ci≤10^9)描述。如果 ti 是 0,那么当收银员扫描 商品i时,Bob 不能偷任何东西。 输出格式 输出一个数字——Bob需要支付的最小金额是多少。
题目链接
http://codeforces.com/problemset/problem/19/B
这一道题十分巧妙,我们可以把这道题的模型转化成01背包的模型。
怎么转化呢?因为Bob可以一秒偷一个,那么买一个物品i,他就可以偷ti个,加上他买过的一个,那么就可以获得ti+1个,那么,我们最后所需的不就是获得n个是的最小值吗?
我们就将ti+1当成体积,ci当成代价,01背包就可以了。
但是,如果背包剩余空间不够当前操作导致无法求出最优解呢?那么就有各种方法来处理啦,这一个细节看代码吧。
tourist的代码
1 {$R+,S+,Q+,I+,O-} 2 {R-,S-,Q-,I-,O+}//我至今没明白这是干什么的 3 const inf = round(1e18); 4 var 5 n,i,t,c,j,q: longint; 6 f: array [0..2010] of int64; 7 ans: int64; 8 begin 9 // assign(input,'in'); reset(input); 10 // assign(output,'out'); rewrite(output); 11 read(n); 12 for i:=1 to n do f[i]:=inf;//全赋值为最大值 13 f[0]:=0; 14 ans:=0; 15 for i:=1 to n do 16 begin 17 read(t,c); 18 inc(t); 19 for j:=n-1 downto 0 do 20 begin 21 q:=j+t; 22 if q > n then q:=n;//特判体积总和大于n的情况,直接赋值为n 23 if f[j]+c < f[q] then f[q]:=f[j]+c; 24 end; 25 inc(ans,c); 26 end; 27 writeln(f[n]); 28 close(input); 29 close(output); 30 end.
hoslyric的代码
using namespace std; typedef unsigned uint; typedef long long Int; typedef vector<int> vint; typedef pair<int,int> pint; #define mp make_pair template<class T> void pv(T a, T b) { for (T i = a; i != b; ++i) cout << *i << " "; cout << endl; } int in_c() { int c; for (; (c = getchar()) <= ' '; ) { if (!~c) throw ~0; } return c; } int in() { int x = 0, c; for (; (uint)((c = getchar()) - '0') >= 10; ) { if (c == '-') return -in(); if (!~c) throw ~0; } do { x = (x << 3) + (x << 1) + (c - '0'); } while ((uint)((c = getchar()) - '0') < 10); return x; } const Int INF = 1001001001001001001LL; void chmin(Int &t, Int f) { if (t > f) t = f; } const int LIM = 4005; int N; int T[2010]; Int C[2010]; Int dp[4010]; int main() { int i, j; N = in(); for (i = 0; i < N; ++i) { T[i] = in() + 1; C[i] = in(); } for (j = 0; j <= LIM; ++j) { dp[j] = INF; } dp[0] = 0; for (i = 0; i < N; ++i) { for (j = LIM; j >= T[i]; --j) { chmin(dp[j], dp[j - T[i]] + C[i]);//不管是否大于n,直接背包到最大可能体积 } } Int ans = INF; for (j = N; j <= LIM; ++j) { chmin(ans, dp[j]);//求合法情况中的最小值 } cout << ans << endl; return 0; }
Zhukov_Dmitry的代码:他的思路和前面说的不一样,他的思路是设d[i][j]为到了当前节点,还有j个物品可以偷是的最小值(其实就是把之前买过物品偷的时间累积起来,需要再用),动规公式也很好推,但要注意0下标的偏移。
using namespace std; #define bublic public #define clr(x) memset((x), 0, sizeof(x)) #define all(x) (x).begin(), (x).end() #define pb push_back #define mp make_pair #define sz size() #define For(i, st, en) for(int i=(st); i<=(int)(en); i++) #define Ford(i, st, en) for(int i=(st); i>=(int)(en); i--) #define forn(i, n) for(int i=0; i<(int)(n); i++) #define ford(i, n) for(int i=(n)-1; i>=0; i--) #define fori(it, x) for (__typeof((x).begin()) it = (x).begin(); it != (x).end(); it++) template <class _T> inline _T sqr(const _T& x) { return x * x; } template <class _T> inline string tostr(const _T& a) { ostringstream os(""); os << a; return os.str(); } typedef long double ld; // Constants const ld PI = 3.1415926535897932384626433832795; const ld EPS = 1e-11; // Types typedef signed long long i64; typedef unsigned long long u64; typedef set < int > SI; typedef vector < int > VI; typedef map < string, int > MSI; typedef pair < int, int > PII; int n; int a[2048][2]; i64 d[2048][4048]; int main() { #ifdef ROOM_311 freopen("input.txt", "rt", stdin); freopen("output.txt", "wt", stdout); #endif cout << setiosflags(ios::fixed) << setprecision(10); scanf("%d", &n); forn(i, n) { scanf("%d%d", &a[i][0], &a[i][1]);//此处a[i][0]并没有+1 } memset(d, 0x3f, sizeof(d)); d[0][2020] = 0;//将0下标点偏移值2020(可用来处理负下标) forn(i, n) { forn(j, 4021) { if (j) { d[i+1][j-1] = min(d[i+1][j-1], d[i][j]);//这一个地方意思是如果当前不买的话,那么可以偷走的个数就要-1 } int jn = j + a[i][0]; if (jn > 4020) jn = 4020; d[i+1][jn] = min(d[i+1][jn], d[i][j] + a[i][1]);//正常处理买过了的情况 } } i64 ans = 0x3f3f3f3f3f3f3f3fLL; forn(i, 2001) { ans = min(ans, d[n][2020 + i]);//对于所有的可能的情况去最小值 } printf("%I64d\n", ans); return 0; }
rng_58的代码
using namespace std; #define REP(i,n) for((i)=0;(i)<(int)(n);(i)++) #define foreach(c,itr) for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++) template <class T> inline string itos(T n) {return (n)<0?"-"+itos(-(n)):(n)<10?(string)("")+(char)('0'+(n)):itos((n)/10)+itos((n)%10);} typedef long long ll; #define INF (1ll<<60) int t[2010]; ll c[2010]; ll dp[2010][2010]; // pos, tsum int main(void){ int N,i,j; cin >> N; REP(i,N) {cin >> t[i] >> c[i]; t[i]++;} REP(i,N+1) REP(j,N+1) dp[i][j] = INF; dp[0][0] = 0; REP(i,N) REP(j,N+1){ dp[i+1][j] = min(dp[i+1][j],dp[i][j]);//当前物品不买 int j2 = min(j+t[i],N);// 防止溢出 dp[i+1][j2] = min(dp[i+1][j2],dp[i][j]+c[i]);//当前物品买 } cout << dp[N][N] << endl;//输出最小值 return 0; }