2165: 黄金矿工
Time Limit: 1 Sec Memory Limit: 128 MB
Submit: 58 Solved: 28
[Submit][Status][Web Board]
Description
Input
3 10
1 1 1 1
2 2 2 2
1 3 15 9
Output
3
Sample Input
Sample Output
HINT
-----sample2------
1 1 13 1
2 2 2 2
1 3 4 7
-----sample2-----
7
-----------
30%的数据,0 < T ≤ 4000
100%的数据,N ≤ 200, 0 < T ≤ 40000
Source
【解析】
先处理每个点,算出每个点的斜率和距离原点的距离
然后先按斜率为第一顺序排序,然后是距离。
接着就是01背包问题了。
但是有点不同的是在相等角度的情况下,后者加上前者的 t 和 v(因为先抓前面那个才能抓后面的),把它看成一个整体的状态来思考,最后通过 mk[i] 来控制跳跃到上一个不相等的元素。
#include<bits/stdc++.h>
using namespace std;
const int maxt = 400 + 10;
struct node
{
double a;
int t, v, s;
};
int cmp(node x, node y)
{
if (x.a == y.a)
return x.s < y.s;
return x.a < y.a;
}
int main()
{
node nds[300];
int n, m, x, y;
int dp[300][maxt];//黄金数,时间
while (~scanf("%d%d", &n, &m))
{
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
{
scanf("%d%d%d%d", &x, &y, &nds[i].t, &nds[i].v);
nds[i].a = y * 1.0 / x;
nds[i].s = y * y + x * x;
}
sort(nds + 1, nds + 1 + n, cmp);//排序,角度优先,其次是距离原点的距离
int idx = 1, mk[300];
mk[1] = 1; // 与 nds[i] 同步
for (int i = 2; i <= n; i++)
if (nds[i].a != nds[i - 1].a)//角度不同编号不同
mk[i] = ++idx;
else
mk[i] = idx;//角度相同编号相同
for (int i = 1; i <= n; i++)
{
if (nds[i].a == nds[i - 1].a)//在同一条直线上(肯定是躲在后面那个)
{
nds[i].t += nds[i - 1].t;//在同一直线的时间加到后面那个一起
nds[i].v += nds[i - 1].v;//同一直线的价值加到后面那个一起
for (int j = m; j >= 0; j--)//01背包模板
{
if (j >= nds[i].t)
{
dp[i][j] = max(dp[i - 1][j], dp[mk[i] - 1][j - nds[i].t] + nds[i].v); // mk[i]-1 可以打印dp看看分析下,因为要跳过前一个相同的,因为已经把前一个的t和v加上去了,表示把多个看为一个整体
//cout << mk[i] - 1 << " mk" << endl;
}
else
dp[i][j] = dp[i - 1][j];
}
}
else//不在同一条直线上
for (int j = m; j >= 0; j--)
{
if (j >= nds[i].t)
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nds[i].t] + nds[i].v);
else
dp[i][j] = dp[i - 1][j];
}
}
printf("%d\n", dp[n][m]);
}
return 0;
}