柱爷很忙
Time Limit: 1000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others)
柱爷很忙。
柱爷每天不仅要炒股,还要策划和实施抢银行,同时还要水群、回答IOI小朋友们问题,又或者要练习咸鱼神功。
为了能更快完成这些事,把时间拿来玩BattleBlock Theater,柱爷把要做的 N N件事的类型 ai ai按原本做的顺序记录下来,计算最少要花费的时间。
万事冥冥之中俱有关联。假如柱爷做事 i i的前一件事是事 j j,所花费的时间为 (ai|aj)−(ai&aj) (ai|aj)−(ai&aj),其中 |,& |,&是位运算符。若柱爷做事 i i前没有做事,则柱爷要花费的时间为 ai ai。
但事情总有轻重缓急之分,按原本顺序的事 i i最多能推迟到做完任意件紧接着事 i i之后的事 j,i<j≤i+bi j,i<j≤i+bi后做,即原本顺序的事 k,k>i+bi k,k>i+bi不能在事 i i之前完成。
柱爷已经知道自己最少要花多少时间了,请问你知道吗。
Input
第一行一个数 N N,代表柱爷要做 N N件事。 1≤N≤1000 1≤N≤1000
之后 N N行,每行两个数 ai,bi ai,bi,代表事情的类型和重要程度。 1≤ai≤1000,0≤bi≤7
Output
输出一行一个数,即柱爷最少要花的时间。
题解
由于b <= 7,我们可以使用使用装压。dp[i][j][k],表示考虑前i件事,状态为j,前一件做的事距i为k。
我们考虑对于第i件事,如果它能拖,进行转移。之后考虑做j中的事情进行转移。
具体不好描述,直接上代码。
#include <bits/stdc++.h>
using namespace std;
const int Maxn = 1010, INF = 0x7f7f7f7f;
int a[Maxn], b[Maxn];
int dp[Maxn][(1 << 8) + 10][22];
void SelfMin(int &a, const int &b) {
if (b < a) a = b;
}
int Cal(const int &a, const int &b) {
return (a | b) - (a & b);
}
int main() {
int n, all = 1 << 8;
scanf("%d", &n);
memset(dp, 0x7f, sizeof dp);
dp[0][all - 1][19] = 0;
for (int i = 1; i <= n; ++i) scanf("%d%d", a + i, b + i);
for (int i = 0; i <= n; ++i)
for (int j = 0; j < all; ++j)
for (int k = 0; k < 20; ++k)
if (dp[i][j][k] != INF) {
int ed = min(8, i), CanOff = INF;
for (int p = ed - 1; p >= 0; --p)
if (((j >> p) & 1) == 0) SelfMin(CanOff, i - p + b[i - p]);
if ((j >> 7) & 1) SelfMin(dp[i + 1][(j ^ (1 << 7)) << 1][min(k + 1, 19)], dp[i][j][k]);
for (int p = ed - 1; p >= 0 && i - p <= CanOff; --p)
if (((j >> p) & 1) == 0)
SelfMin(dp[i][j | (1 << p)][p],
dp[i][j][k] + ((k == 19) ? a[i - p] : Cal(a[i - p], a[i - k])));
}
int ans = INF;
for (int i = 0; i < 20; ++i) SelfMin(ans, dp[n][all - 1][i]);
cout << ans;
return 0;
}