题意:有n个金矿,每个金矿有抓取的消耗的时间和价值,矿工在原点,问在T时间内能得到的最大的价值
分析:唯一和01背包不同的是金矿可能共线,也就是抓取近的金矿后才能抓后面共线的金矿。这是分组背包问题,方法是将点按照斜率排序,如果相等按照距离原点远近排序,将斜率相等的点分成一组,每组的点累加上前面的点的时间和价值,这样每组只选一个点,就是01背包了
收获:分组背包问题
代码:
/************************************************ * Author :Running_Time * Created Time :2015-8-27 8:48:05 * File Name :J.cpp ************************************************/ #include <cstdio> #include <algorithm> #include <iostream> #include <sstream> #include <cstring> #include <cmath> #include <string> #include <vector> #include <queue> #include <deque> #include <stack> #include <list> #include <map> #include <set> #include <bitset> #include <cstdlib> #include <ctime> using namespace std; #define lson l, mid, rt << 1 #define rson mid + 1, r, rt << 1 | 1 typedef long long ll; const int N = 2e2 + 10; const int INF = 0x3f3f3f3f; const int MOD = 1e9 + 7; const double eps = 1e-6; struct Point { int x, y, t, v; bool operator < (const Point &r) const { if (y * r.x == x * r.y) return y < r.y; else return y * r.x < x * r.y; } bool operator == (const Point &r) const { return y * r.x == x * r.y; } }p[N]; int dp[40010]; vector<Point> block[N]; int main(void) { int n, T, cas = 0; while (scanf ("%d%d", &n, &T) == 2) { for (int i=1; i<=n; ++i) { scanf ("%d%d%d%d", &p[i].x, &p[i].y, &p[i].t, &p[i].v); } sort (p+1, p+1+n); int cnt = 0; for (int i=1; i<=n; ++i) block[i].clear (); block[++cnt].push_back (p[1]); for (int i=2; i<=n; ++i) { if (p[i] == p[i-1]) block[cnt].push_back (p[i]); else block[++cnt].push_back (p[i]); } for (int i=1; i<=cnt; ++i) { for (int j=1; j<block[i].size (); ++i) { block[i][j].t += block[i][j-1].t; block[i][j].v += block[i][j-1].v; } } memset (dp, 0, sizeof (dp)); for (int i=1; i<=cnt; ++i) { for (int j=T; j>=0; --j) { for (int k=0; k<block[i].size (); ++k) { if (j >= block[i][k].t) dp[j] = max (dp[j], dp[j-block[i][k].t] + block[i][k].v); } } } printf ("Case %d: %d\n", ++cas, dp[T]); } return 0; }
时间: 2024-10-14 17:07:43