Hrbust 1255/CSU 1070 Knight Match Play【思维+Bfs预处理+Kmp】好题~

224 篇文章 2 订阅
15 篇文章 0 订阅

Knight Match Play
Time Limit: 1000 MSMemory Limit: 65536 K
Total Submit: 31(5 users)Total Accepted: 5(4 users)Rating: Special Judge: No
Description
There are two knight teams, they have N and M knights respectively. The knights are numbered from 1 to N and 1 to M in their own team. They are trapped in a W*H grid puzzle. The puzzle only has four exituses which are the four corners of the puzzle. Only the directions up,down,left,right could the knight move to and the time move from one grid to the adjacent is fixed. They can form a "fight partner" if two knights sufficing the following conditions:
1, The two knights come from different teams.
2, Both of them can arrive to one of the exituses.
3, The least time need to arrive to one of the exituses is same.

So how many "fight partners" they can form?

1255

For this action, the leader gives the follow directives:
1, Each team renumbering the knights with their least time need to use to get out of the puzzle. So the one who use the least time will get the ID 1 and the one use the most time will get the ID N or M. If some of them use the same time, then whose original ID is lower, who will get a lower ID.
2, The chosen knights from the same team should have the continuous ID.
3, Form "fight partners" according to the IDs one by one. That is to say the one has the lowest ID should form with the one has the lowest ID in another team, and so on.

Input
There are several test cases. The input of every test case are described as below.
First line, there are four integers N,M,W,H. (0<n,m<300, 10<w,h<50).
 Then there are H lines, each line with W characters indicate for the puzzle. A character of '.' indicate the grid is empty, every empty grid could contain infinitely of knights. A character of '#' means the grid has a barrier and none knight can step into it. The four corners are empty. 
Then there N+M lines, each line with a pair of integers (x,y)(0<=x<h,0<=y<w) originally.
 The input will finish with the end of file
Output

An integer indicate for the maximal number of "fight partner" they can form.

Sample Input

4 3 7 6
.#...#.
..#....
.......
..#....
....##.
.##....
1 3
4 2
2 2
1 5
3 5
4 3
3 0

Sample Output

3


Source
Hunan University 2011 the 7th Programming Contest
Recommend
万祥

题目大意:

给你一个H*W大小的一个迷宫,其中#表示不能走,“.”表示可以走,

现在迷宫的出口在四个角落(左上.左下.右上.右下);

我们规定一共有两活人,一伙N个,一伙M个,初始的时候编号按照输入顺序编号.

现在规定合作伙伴:

①合作伙伴是两个人,并且来自不同的阵营。

②两个人到达其最近的出口耗时相同。

然后我们需要将两个阵营的骑士重新编号。

重新编号的规则按照:按照逃出迷宫从小到大的时间排序,然后按照这个顺序从新编号。对于时间相同的,按照原序列优先级编号。

然后得到两个重新编号的数组A.B.让你在任意一个数组中取任意一段连续子序列,让其是另外一个数组的子串,求这个合法子段的最长长度。


思路:


1、根据手动测试数据范围已知,n,m不大于300.W.H不大于500.


2、题目很复杂,我们首先简化问题,其实就是让你①先求出每个人逃出迷宫的时间,然后按照时间点从小到大排序得到两个数组,②然后求一个数组任意取一段连续子段是另外一个数组的子串即可,我们需要求一个最大长度。


3、那么我们首先处理前部分任务:

我们对于一个图的遍历的时间复杂度是O(nm)如果我们对于每一个骑士进行Bfs,肯定是超时的,那么我们Bfs四个起点,维护一个数组step【i】【j】.表示从任意起点到(i,j)这个位子的最短时间消耗,反过去考虑,step【i】【j】其实就是从(i,j)这一点,到任意出口的最小时间花费。

那么对于每个骑士来讲,其逃出的时间,就是step【x】【y】;

这部分时间复杂度:O(nm);


4、然后我们处理后置任务。

①对于我们处理出来的两个数组,按照时间从小到大先排个序,模拟题目要求。

②考虑到KMP匹配的特性,我们只需O(n)枚举A.B数组中较小的那个序列的起点,去匹配另一个序列即可,过程维护一个最大匹配长度值。

③这里有一个trick.输入数据不保证一个点一定能够走到出口。所以从小到大排序结束之后,要重新界定一下数组的大小(就是要把不能走到终点的那些去除掉)。

④过程维护一个最大值即可。

时间复杂度O(min(lena,lenb)*(lena+lenb));


5、总体来说题目比较综合,而且坑点十足,尼玛对拍数据都调了很久才过,给这个题一个好评吧,估计也没几个人能做这个题了(- -);


Ac代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
struct node
{
    int x,y;
}now,nex;
char mp[505][505];
int step[505][505];
int numa[505];
int numb[505];
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};
int a[500];
int b[500];
int naxt[2500];
int conta,contb,n,m;
int lena,lenb;
int Bfs()
{
    queue<node >s;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            step[i][j]=0x3f3f3f3f;
            if((i==0&&j==0)||(i==n-1&&j==m-1)||(i==n-1&&j==0)||(i==0&&j==m-1))
            {
                now.x=i;
                now.y=j;
                step[i][j]=0;
                s.push(now);
            }
        }
    }
    while(!s.empty())
    {
        now=s.front();
        s.pop();
        for(int i=0;i<4;i++)
        {
            nex.x=now.x+fx[i];
            nex.y=now.y+fy[i];
            if(nex.x>=0&&nex.x<n&&nex.y>=0&&nex.y<m&&mp[nex.x][nex.y]!='#')
            {
                if(step[nex.x][nex.y]>step[now.x][now.y]+1)
                {
                    step[nex.x][nex.y]=step[now.x][now.y]+1;
                    s.push(nex);
                }
            }
        }
    }
    return 0;
}
void set_naxt()//子串的naxt数组
{
    int i=0,j=-1;
    naxt[0]=-1;
    while(i<lenb)
    {
        if(j==-1||b[i]==b[j])
        {
            i++; j++;
            naxt[i]=j;
        }
        else
        j=naxt[j];
    }
}

int KMP()
{
    int maxnlen=0;
    int i=0,j=0;
    set_naxt();
    while(i<lena)
    {
        if(j==-1||a[i]==b[j])
        {
            i++;j++;
            maxnlen=max(j,maxnlen);
        }
        else
        {
            maxnlen=max(j,maxnlen);
            j=naxt[j];
        }
        if(j==lenb)
        {
            return lenb;
        }
    }
    return maxnlen;
}
int main()
{
    while(~scanf("%d%d%d%d",&conta,&contb,&m,&n))
    {
        for(int i=0;i<n;i++)scanf("%s",mp[i]);
        Bfs();
        for(int i=0;i<conta;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            numa[i]=step[x][y];
        }
        for(int i=0;i<contb;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            numb[i]=step[x][y];
        }
        sort(numa,numa+conta);
        sort(numb,numb+contb);
        for(int i=0;i<conta;i++)
        {
            if(numa[i]==0x3f3f3f3f)
            {
                conta=i;
                break;
            }
        }
        for(int i=0;i<contb;i++)
        {
            if(numb[i]==0x3f3f3f3f)
            {
                contb=i;
                break;
            }
        }
        int output=0;
        if(conta<contb)
        {
            lena=contb;
            for(int i=0;i<contb;i++)
            {
                a[i]=numb[i];
            }
            for(int i=0;i<conta;i++)
            {
                lenb=0;
                for(int j=i;j<conta;j++)
                {
                    b[lenb++]=numa[j];
                }
                output=max(KMP(),output);
            }
        }
        else
        {
            lena=conta;
            for(int i=0;i<conta;i++)
            {
                a[i]=numa[i];
            }
            for(int i=0;i<contb;i++)
            {
                lenb=0;
                for(int j=i;j<contb;j++)
                {
                    b[lenb++]=numb[j];
                }
                output=max(KMP(),output);
            }
        }
        printf("%d\n",output);
    }
}










评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值