zoukankan      html  css  js  c++  java
  • [CF1523F] Favorite Game

    前言

    妙妙题!

    题目

    CF

    洛谷

    题目大意:

    现在有一只酷酷的柒处在二维平面上,上面有 (n) 个传送门和 (m) 个有刺杀地点。柒每秒可以移动到相邻的一格(四个方向)或者不动,如果 (t_i) 时刻柒在某一个刺杀地点,那么他就可以完成这个刺杀任务(没听过柒还能失手)。

    作为玄武国的刺客,他有权在任意地点传送到传送门 (i),只不过传送门是新修建的,所以需要走到传送门 (i) 激活之后才能使用。

    作为首席刺客,他的行踪变幻莫测,所以他初始位置是可以任意选择的。

    柒最多可以完成多少个刺杀任务?

    (0le nle 14;1le mle 100;1le x_i,y_ile 10^6;1le t_ile 10^9.)

    夹带私货。

    讲解

    我们把传送门和刺杀地点统称为地点,把 (t_i) 称为刺杀时间

    当然是考虑状压DP。

    朴素一点,我们先令 (dp_{s,i,j}) 表示经过的传送门集合为 (s),现在在第 (i)地点,完成了 (j) 个任务 的最小时间。

    状态数就达到了惊人的 (2^n imes (n+m) imes 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 imes (n+m) imes m))

    代码

    //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;
    }
    

    细节

    • 答案最小值为 (1)
    • 从刺杀地点出发并不一定要使用传送门,还有直接步行的选项,注意时间要取最小值。
    • (f_{s,i}) 中的 (iin[0,m]) 而不是 ([0,n])
  • 相关阅读:
    设计模式享元模式实现C++
    并查集
    设计模式代理模式实现C++
    设计模式装饰模式实现C++
    最小生成树Prim算法实现
    图的邻接矩阵存储
    威佐夫博弈(Wythoff Game)初识 HDU 1527 POJ 1067
    设计模式原型模式实现C++
    三种经典博弈问题 BashGame;WythoffGame;NimmGame;
    设计模式外观模式实现C++
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/14842252.html
Copyright © 2011-2022 走看看