Codeforces Round #533 (Div. 2) D. Kilani and the Game(双队列多源bfs)

题目链接:http://codeforces.com/contest/1105/problem/D

题目大意:

       给一个地图,地图上有#(不可走),数字,和点组成,每种数字代表一种帮派。然后按照1-2-3.。。。。的顺序扩张领地,然后再1 2 3的扩张。每个帮派有一个扩张速度,直到最后无地可走,输出每个帮派各有多少领地。

思路:(读者可以从绿色字体开始往下读,前半部分为错误思路的分析

        起初我想的是可以用双端队列,把扩张速度可以看作扩张次数,然后如果双端队列的队首元素还有大于1的可扩张次数,就入队首,并且扩张次数减一,直到扩张次数为1,入队尾。这样做想法可行,但是实现的时候我们会发现一个尴尬的问题,后到一个点如果还有的扩张次数比原来的大,但是那个点已经被vis掉了怎么办??好解决。只要vis数组记录一下这个点的可扩张次数,如果这个点是“自己之前扩张的领地”且,可扩张次数比这个大,就再次入队。想法又可行了,但是超时在test20了。

      这是怎么回事呢?比如这组样例

5    5    2
100    1
2.1.1
1.1.1
1.1.1
1.1.1
1.1.1

如果用的双端队列,那么我们从第一个1开始搜索,也就是(1,3)的位置。。搜索开始!!!(出队过程忽略不写)

1.(1,2)入队首(1,4)入队首,两个点的可扩张次数为99

2.(2,4)入队首.。。。(3,4)入队首。。。(等等!!为什么是(2,4)入队不是(2,2),要知道我们在不断地入队首,所以又不断地从队首取元素,所以双端队列的队首部分成为了一个栈!!!)

3. 。。。。直到(5,4)入队然后此处省略若干字。。(5,2)入队。。到目前为止我们只考虑了一个点,而这些走过的点在下边又会碰到更优的点(存在更近的源点)从而重复入队

于是开始胡思乱想。。。。。。。(以下是才正解)

       只要我解决了双端队列的头是个栈的问题是不是就可以优化了,并且不需要重复入队。。好就这么干。取消双端队列改为用两个队列q和q1,bfs的时候在q队列中的元素的可扩张次数都为他的最大的扩张次数,也就是每轮的开始,而q1,就是每轮每个帮派的辅助队列以便进行一下的s次扩充。我们从q中出一个然后把能到的入到q1中,直到换q中换下一个帮派的时候我们,把q1中存的上个帮派存的结点全部扩充。如果扩充次数大于1就还入q1,如果不是1就入队q。但是注意!!!我们的q空了之后,还要清空q1一遍,不然会漏点。

       我们的q1辅助队列帮我们解决了双端队列的队首是栈且可重复入队的弊端。虽然表面上多加了一个循环但是却做出了优化。很棒。。而且这样的最大好处就是入队条件不用那些 花里胡哨的只需要判断这个点在不在地图里头,和是否走过就可以。不用判断重复入队,因为这就是简简单单(并不)地模拟了题目的扩张步骤。

所以双端队列+栈应该也可以过(纯粹口胡。)

#include<iostream>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<stdio.h>
#include<queue>
#define ll long long
#include<vector>
using namespace std;
const int MAXN=1005;
int n,m,p;
char C[1005][1005];
int ans[20];
int vis[1005][1005];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
struct Point
{
    int x,y,s,num;//x,y为坐标,s为扩张次数,num为帮派
    Point(){}
    Point(int x1,int y1,int s1,int num1){x=x1;y=y1;s=s1;num=num1;}
};
bool check(int x,int y)
{
    if(x>=1&&x<=n&&y>=1&&y<=m)return 1;
    else return 0;
}
int sp[20];
vector<Point>v[20];
void bfs()
{
    queue<Point>q,q1;//q为每轮扩张的源点队列,q1为每个帮派每次扩长的辅助队列
    for(int i=1;i<=p;i++){//用vector存第一次的地图上的扩张源点
        int len=v[i].size();
        for(int j=0;j<len;j++){
            q.push(v[i][j]);
        }
    }
    int lun=1;
    while(!q.empty()){
        Point now=q.front();
        q.pop();
        if(now.num!=lun){
            while(!q1.empty()){
                Point now2=q1.front(); 
                q1.pop();
                for(int i=0;i<4;i++){
                    int xx=now2.x+dx[i];
                    int yy=now2.y+dy[i];
                    if(check(xx,yy)&&!vis[xx][yy]){
                        vis[xx][yy]=1;
                        C[xx][yy]=now2.num+'0';
                        if(now2.s>1)q1.push(Point(xx,yy,now2.s-1,now2.num));
                        else q.push(Point(xx,yy,sp[now2.num],now2.num));
                    }
                }
            }
        }
        lun=now.num;
        for(int i=0;i<4;i++){
            int xx=now.x+dx[i];
            int yy=now.y+dy[i];
            if(check(xx,yy)&&!vis[xx][yy]){
                vis[xx][yy]=1;
                C[xx][yy]=now.num+'0';
                if(now.s>1)q1.push(Point(xx,yy,now.s-1,now.num));
                else q.push(Point(xx,yy,sp[now.num],now.num));
            }
        }
    }
    while(!q1.empty())//此时q空了,但是q1没空记得再清一次
    {
        Point now2=q1.front(); 
        q1.pop();
        for(int i=0;i<4;i++){
            int xx=now2.x+dx[i];
            int yy=now2.y+dy[i];
            if(check(xx,yy)&&!vis[xx][yy]){
                vis[xx][yy]=1;
                C[xx][yy]=now2.num+'0';
                if(now2.s>1)q1.push(Point(xx,yy,now2.s-1,now2.num));
                else q.push(Point(xx,yy,sp[now2.num],now2.num));
            }
        }
    }
}
int main()
{
    memset(ans,0,sizeof(ans));
    memset(vis,0,sizeof(vis));
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=p;i++)
        scanf("%d",&sp[i]);
    for(int i=1;i<=n;i++){
        scanf("%s",C[i]+1);
        for(int j=1;j<=m;j++){
            if(C[i][j]!='.')vis[i][j]=1;
            if(C[i][j]>='1'&&C[i][j]<='9'){
                int now=C[i][j]-'0';
                v[now].push_back(Point(i,j,sp[now],now));
            }
        }
    }
    bfs();
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++){
            if(C[i][j]>='0'&&C[i][j]<='9')ans[C[i][j]-'0']++;
           // cout<<C[i][j]<<"+";
        }
       // cout<<endl;
    }
    for(int i=1;i<=p;i++)
    {
        printf("%d ",ans[i]);
    }
    printf("\n");
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值