zoukankan      html  css  js  c++  java
  • 赛马场上的术士

    前言

    在同一个OJ,同样的C++11,几份代码对比如下:

    std (O(Tn^2log_2n)) 5634ms,46.46MB.

    OneInDark 对题解做法进行优化,(O(Tnlog_2^2n)) 130ms,520KB.

    (O(Tn^2)) 115ms,2656KB.

    如果发现我的做法有问题,欢迎 Hack。

    好家伙,不到一个小时就被 Hack 了,锅已经修了。

    好,又被 Hack 了,贪心思路应该没什么问题,具体实现我锅了,现在又修好了。(只改了出锅的L,等E出锅了再改

    代码已经改得面目全非了QAQ

    题目

    ( t 3s 256MB)

    小P找到了 ( t T) 个全是术士的赛马场,由于对术士的马速深恶痛绝,所以他在门口装了一个监控以监视术士的行踪,最初赛马场内部的情况是未知的。

    今天小P监视到了术士的 ( t N) 个行动,可以表示为:

    • ( t E ID),表示编号为 ID 的术士进入了赛马场;当 ( t ID)(0) 时,表示一个术士伪装后进入了赛马场。
    • ( t L ID),表示编号为 ID 的术士离开了赛马场;当 ( t ID)(0) 时,表示一个术士伪装后离开了赛马场。

    小P想知道赛马场是否一定有其他出入口。

    如果有,输出 OTHER,如果没有的话,小P想知道一天结束后赛马场中最少可能会有多少术士。

    以便小P在此时进入赛马场而承担最少的风险。(如果你不知道炉石传说赛马场上的集结,你可以忽略这句话)

    (1le t Nle 1000;1le t Tle 10;0le t IDle 2000.)

    你可以结合样例来更好地理解题意:

    样例输入

    2
    3
    E 5
    L 0
    E 5
    2
    L 1
    L 1
    

    样例输出

    1
    OTHER
    

    讲解

    这是一个复杂度比标程优,和标程拍了 (7000+) 组无锅的做法。

    为了方便,我们把赛马场存在多个出入口称为无解。

    无解情况

    首先我们考虑可能出现无解的情况有且仅有两种:一个人连续进入或连续离开赛马场两次。

    对于一个 ( t ID_i>0) 的操作,我们找到上一个 ( t ID_j=ID_i,j<i) 的位置。

    也就是说 ( t j,i) 是两个相邻的且 ( t ID) 相同的数字。

    • ( t E E) 情况,我们需要在中间找一个 ( t L),因为 ( t j,i) 是两个相邻的且对应 ( t ID) 相同的位置,所以此时找到的 ( t L) 对应的 ( t ID) 一定是 (0),因此找的 ( t L) 越靠前越好,找不到即无解。
    • ( t L L) 情况,我们需要在中间找一个 ( t E),同理其对应的 ( t ID) 也一定是 (0),所以找的 ( t E) 越靠后越好,找不到即无解。
    • ( t E L) 情况,我们对 ( t L) 打上一个标记,表示其可以匹配前面的一个 ( t E),注意不是直接匹配
    • ( t L E) 情况,跳过。

    贪心匹配

    无解情况 中,如果我们发现是有解的,其中的操作相当于把所有必须匹配的 ( t E)( t L) 匹配上了,接下来我们要做的就是尽可能多的匹配 ( t E L)

    因为对答案有贡献的是未被匹配的 ( t E),所以我们枚举的应该是 ( t E),而且不难发现我们需要从后往前枚举。

    此时我们分情况讨论优先级,下文的 ( t x,y) 表示某个非 (0) 数。

    ( t E 0) 情况:

    任何 ( t L) 都可以和它匹配,而 ( t L 0) 是万能的,可以和任何 ( t E) 匹配,所以我们尽可能多的留下 ( t L 0),于是我们优先找 ( t L x) 与其匹配,但 ( t L x) 中也有优先级问题,还记得我们之前打的标记吗,我们没有打上标记的 ( t L y) 的前面找不到一个对应的 ( t E y) 与其匹配,只能匹配 ( t E 0)

    所以此时的匹配优先级为: 没有标记的 ( t L y) ,有标记的 ( t L x),最后是 ( t L 0)

    ( t E x) 情况:

    此时只有对应的 ( t L x) 和万能的 ( t L 0) 能与其匹配。

    显然优先级为:( t L x),然后再找 ( t L 0)

    代码

    贴心注释版。
    //12252024832524
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define TT template<typename T>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 2005;
    int n,a[MAXN],dn[MAXN],ID[MAXN];
    bool mat[MAXN];
    char c[MAXN];
    
    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;}
    
    bool vis[MAXN][MAXN];
    bool solve(int x)
    {
    	int i = dn[x];
    	if(vis[i][x]) return 0;
    	vis[i][x] = 1;
    	for(int j = x-1;j >= ID[i];-- j)
    	{
    		if(vis[i][j]) return 0;
    		if(c[j] == 'E' && !a[j] && !dn[j])
    		{
    			dn[j] = dn[i] = i;
    			return 1;
    		}
    	}
    	for(int j = x-1;j >= ID[i];-- j)
    		if(c[j] == 'E' && !a[j] && dn[j])
    		{
    			if(solve(j))
    			{
    				dn[j] = dn[i] = i;
    				return 1;
    			}
    		}
    	return 0;
    }
    
    int main()
    {
    //	freopen("probe.in","r",stdin);
    //	freopen("probe.out","w",stdout);
    	for(int T = Read(); T ;-- T)
    	{
    		n = Read();
    		for(int i = 1;i <= n;++ i)
    		{
    			c[i] = getchar();
    			while(c[i] != 'E' && c[i] != 'L') c[i] = getchar();
    			a[i] = Read(); 
    			dn[i] = mat[i] = 0;
    			ID[i] = 0;
    		}
    		for(int i = 1;i <= n;++ i)
    			for(int j = i;j <= n;++ j)
    				vis[i][j] = 0;
    		bool ots = 0;
    		for(int i = 1;i <= n;++ i)
    		{
    			if(!a[i]) continue;
    			for(int j = i-1;j >= 1 && !ID[i];-- j)
    				if(a[j] == a[i])
    					ID[i] = j;
    			if(!ID[i]) continue;
    			if(c[i] == 'E')//E 
    			{
    				if(c[ID[i]] == 'E')//E E 找尽量前面的L 0 
    					for(int j = ID[i];j <= i;++ j)
    					{
    						if(c[j] == 'L' && !a[j] && !dn[j])
    						{
    							dn[j] = dn[ID[i]] = i;
    							break;
    						}
    						if(j == i) ots = 1;
    					}
    				if(ots) break;
    			}
    			else //L
    			{
    				if(c[ID[i]] == 'L')//L L 找尽量后面的E 0 
    				{
    					for(int j = i;j >= ID[i];-- j)
    					{
    						if(c[j] == 'E' && !a[j] && !dn[j])
    						{
    							dn[j] = dn[i] = i;
    							break;
    						}
    					}
    					if(dn[i]) continue;
    					for(int j = i;j >= ID[i];-- j)
    					{
    						if(c[j] == 'E' && !a[j] && solve(j))
    						{
    							dn[j] = dn[i] = i;
    							break;
    						}
    						if(j == ID[i]) ots = 1;
    					}
    					if(ots) break;
    				}
    				else mat[ID[i]] = mat[i] = 1;//E x -> L x,打标记 
    			}
    		}
    		if(ots) {printf("OTHER
    ");continue;}
    		for(int i = 1;i <= n;++ i) if(dn[i]) a[i] = -1;
    		int ans = 0;
    		for(int i = n;i >= 1;-- i)
    		{
    			if(a[i] < 0) continue;
    			if(c[i] == 'E') //下面进入复读机模式
    			{
    				if(!a[i])//E 0
    					for(int j = n;j >= i && a[i] >= 0;-- j)
    						if(a[j] > 0 && c[j] == 'L' && !mat[j])//优先找无标记的
    							a[j] = a[i] = -1;
    				if(!a[i])
    					for(int j = n;j >= i && a[i] >= 0;-- j)
    						if(a[j] > 0 && c[j] == 'L')//其次找有标记的
    							a[j] = a[i] = -1;
    				if(a[i])//E x
    					for(int j = n;j >= i && a[i] >= 0;-- j)
    						if(a[j] == a[i] && c[j] == 'L')//优先找 L x
    							a[j] = a[i] = -1;
    				for(int j = n;j >= i && a[i] >= 0;-- j)//实在不行只能由万能的 L 0
    					if(!a[j] && c[j] == 'L')
    						a[j] = a[i] = -1;
    				if(a[i] < 0) continue;
    				++ans;
    			}
    		}
    		Put(ans,'
    ');
    	}
    	return 0;
    }
    

    后记

    如果你对 ( t E x) 不一定和 ( t L x) 优先匹配存疑,不妨看看这组数据:

    输入:

    1
    4
    E 1
    L 0
    E 0
    L 1
    

    输出:

    0
    

    其实我的匹配过程可以再优化。

  • 相关阅读:
    程序调试的利器GDB
    Building a Android Development Environment
    手机的串号IMEI/ESN标示位置图解摩托罗拉官方教程
    Linux调试信息输出串口设备号的设置
    git忽略文件提交
    Spring @Transactional事务传播范围以及隔离级别
    自定义springbootstarter
    maven 命令将jar包安装到本地仓库
    oracle 常用命令
    Oracle 用户(user)和模式(schema)的区别
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15249350.html
Copyright © 2011-2022 走看看