题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4341
分组背包问题, 由于一条直线上的要按照顺序取, 如果一条直线上有n个金矿, 对于这条直线则有n + 1 种决策, 而且是互相矛盾的, 所以可以将这n个物品重新组合一下, 转化成一个分组背包问题, 详见代码。。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
#include <cmath>
#include <cstdlib>
using namespace std;
const int N = 205;
struct Point {
int x, y, t, v;
bool operator <(const Point& p) const {
return labs(x) + labs(y) < labs(p.x) + labs(p.y);
}
};
Point pts[N];
bool flag[N];
vector<Point> group[N];
int dp[40005];
int main() {
int n, W, cas = 1, g;
while (~scanf("%d%d", &n, &W)) {
for (int i = 0; i < n; i++)
scanf("%d%d%d%d", &pts[i].x, &pts[i].y, &pts[i].t, &pts[i].v);
memset(flag, 0, sizeof(flag));
g = 0;
for (int i = 0; i < n; i++)
group[i].clear();
for (int i = 0; i < n; i++) {
if (flag[i]) continue;
Point cur = pts[i];
group[g].push_back(cur);
for (int j = i + 1; j < n; j++)
if (cur.x * pts[j].y - cur.y * pts[j].x == 0 && !flag[j]) {
group[g].push_back(pts[j]);
flag[j] = 1;
}
g++;
}
for (int i = 0; i < g; i++) {
sort(group[i].begin(), group[i].end());
int m = group[i].size();
if (m == 1) continue;
for (int j = 1; j < m; j++) {
group[i][j].t += group[i][j - 1].t;
group[i][j].v += group[i][j - 1].v;
}
}
fill(dp, dp + W + 1, 0);
for (int i = 0; i < g; i++)
for (int j = W; j >= 0; j--)
for (int k = 0; k < group[i].size(); k++)
if (j - group[i][k].t >= 0)
dp[j] = max(dp[j], dp[j - group[i][k].t] + group[i][k].v);
printf("Case %d: %d\n", cas++, dp[W]);
}
return 0;
}