妙妙题!
CF
洛谷
题目大意:
现在有一只酷酷的柒处在二维平面上,上面有 \(n\) 个传送门和 \(m\) 个有刺杀地点。柒每秒可以移动到相邻的一格(四个方向)或者不动,如果 \(t_i\) 时刻柒在某一个刺杀地点,那么他就可以完成这个刺杀任务(没听过柒还能失手)。
作为玄武国的刺客,他有权在任意地点传送到传送门 \(i\),只不过传送门是新修建的,所以需要走到传送门 \(i\) 激活之后才能使用。
作为首席刺客,他的行踪变幻莫测,所以他初始位置是可以任意选择的。
柒最多可以完成多少个刺杀任务?
\(0\le n\le 14;1\le m\le 100;1\le x_i,y_i\le 10^6;1\le t_i\le 10^9.\)
夹带私货。
我们把传送门和刺杀地点统称为地点,把 \(t_i\) 称为刺杀时间。
当然是考虑状压DP。
朴素一点,我们先令 \(dp_{s,i,j}\) 表示经过的传送门集合为 \(s\),现在在第 \(i\) 个地点,完成了 \(j\) 个任务 的最小时间。
状态数就达到了惊人的 \(2^n\times (n+m)\times m\),显然无法通过。
那么如果我们把传送门和刺杀地点分别考虑呢?
如果柒身处传送门,显然我们不需要知道他在哪个传送门,剩余状态:传送门集合、完成任务数。转移的是最小时间。
如果柒身处刺杀地点,他在 \(t_i\) 时刻一定在这个地方,那么时间可以去掉,我们的状态可以为:传送门集合、身处刺杀地点。转移的是完成的任务数。
这样一来,状态都少掉了一维,可以转移了,当然我们要把刺杀地点先按刺杀时间从小到大排序。
令 \(f_{s,i}\) 表示传送门集合为 \(s\),完成任务数为 \(i\),现在身处某一个传送门的最小时间。
令 \(g_{s,i}\) 表示传送门集合为 \(s\),身处刺杀地点 \(i\),可以完成的最大任务数。
为了方便,我们需要预处理一些东西:
令 \(as_{s,i}\) 表示传送门集合为 \(s\),身处某一个传送门,到刺杀地点 \(i\) 所需的最小时间。
令 \(po_{s,i}\) 表示传送门集合为 \(s\),身处某一个传送门,到传送门 \(i\) 所需的最小时间。
现在我们就可以愉快的转移了,共四种情况:从传送门到传送门、从传送门到刺杀地点、从刺杀地点到传送门、从刺杀地点到刺杀地点。
只需要注意时间是否合法,然后即可转移,详见代码。
时间复杂度 \(O(2^n\times m^2)\)。
//12252024832524 #include <cstdio> #include <cstring> #include <algorithm> #define TT template<typename T> using namespace std; typedef long long LL; const int MAXN = 14; const int MAXM = 105; const int MAXS = 1<<MAXN; const int INF = 0x3f3f3f3f; int n,m,S,ans = 1; LL Read() { LL x = 0,f = 1;char c = getchar(); while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();} return x * f; } TT void Put1(T x) { if(x > 9) Put1(x/10); putchar(x%10^48); } TT void Put(T x,char c = -1) { if(x < 0) putchar('-'),x = -x; Put1(x); if(c >= 0) putchar(c); } TT T Max(T x,T y){return x > y ? x : y;} TT T Min(T x,T y){return x < y ? x : y;} TT T Abs(T x){return x < 0 ? -x : x;} int f[MAXS][MAXM],g[MAXS][MAXM]; //portal set & the number of tasks completed : minimal time //portal set & assassination position : maximal number of tasks completed int as[MAXS][MAXM],po[MAXS][MAXN];//assassination & portal struct node { int x,y,t; bool operator < (const node &px)const{ return t < px.t; } }a[MAXN],b[MAXM]; int dis(node A,node B){return Abs(A.x-B.x)+Abs(A.y-B.y);} int main() { // freopen(".in","r",stdin); // freopen(".out","w",stdout); n = Read(); m = Read(); S = (1<<n); for(int i = 0;i < n;++ i) a[i].x = Read(),a[i].y = Read(); for(int i = 1;i <= m;++ i) b[i].x = Read(),b[i].y = Read(),b[i].t = Read(); sort(b+1,b+m+1); for(int s = 0;s < S;++ s) { f[s][0] = INF; for(int i = 1;i <= m;++ i)//to assassination position { as[s][i] = f[s][i] = INF; g[s][i] = -INF; for(int j = 0;j < n;++ j) if((s>>j) & 1) as[s][i] = Min(as[s][i],dis(a[j],b[i])); } for(int i = 0;i < n;++ i)//to portal { po[s][i] = INF; for(int j = 0;j < n;++ j) if((s>>j) & 1) po[s][i] = Min(po[s][i],dis(a[j],a[i])); } } for(int i = 0;i < n;++ i) f[1<<i][0] = 0; for(int i = 1;i <= m;++ i) g[0][i] = 1; for(int s = 0;s < S;++ s) { for(int i = 0;i <= m;++ i) { if(f[s][i] == INF) continue; for(int j = 0;j < n;++ j) if(!((s>>j) & 1)) f[s|(1<<j)][i] = Min(f[s|(1<<j)][i],f[s][i]+po[s][j]); for(int j = 1;j <= m;++ j) { if(f[s][i] + as[s][j] > b[j].t) continue; g[s][j] = Max(g[s][j],i+1); ans = Max(ans,i+1); } } for(int i = 1;i <= m;++ i) { if(g[s][i] == -INF) continue; for(int j = 0;j < n;++ j) { if(b[i].t + Min(po[s][j],dis(b[i],a[j])) >= f[s|(1<<j)][g[s][i]]) continue; f[s|(1<<j)][g[s][i]] = b[i].t + Min(po[s][j],dis(b[i],a[j])); } for(int j = i+1;j <= m;++ j) { if(b[i].t + Min(as[s][j],dis(b[i],b[j])) > b[j].t) continue; g[s][j] = Max(g[s][j],g[s][i]+1); ans = Max(ans,g[s][j]); } } } Put(ans); return 0; }