The 2023 ICPC Asia Hangzhou G题

思路:

一条蛇,满图跑,dp状态太多,只能搜索,分析一下时间复杂度,发现每个点最多出队一次,搜索可行!

怎么搜呢?发现只有三种情况

1.没被身体挡住,直接走

2.被身体挡住了,绕道走

3.被身体挡住了,把蛇砍掉一截再走

维护一个dw数组,表示蛇初始状态时,每个点到尾巴的距离,例如一条长度为3的蛇,从头到尾依次是 (1,1),(1,2),(1,3)

那么dw[1][1]=2,d[1][2]=1,d[1][3]=0,其余的都为0

维护dw数组有什么用?

当一个点搜到了蛇初始状态的某个点时,就要判断,蛇尾离开没,如过离开了就是上述情况1,否则是情况2,3

为什么其余点dw设为0?

因为其余的点必然是蛇头先到,不存在蛇尾有没有离开是情况,所以都设为0

怎么知道什么时候是情况2,什么是时候是情况3?

这个其实不需要人为考虑,交给优先多列考虑就好了

将bfs里面的队列改为优先队列,利用dijsktra的思想,每个点第一次出队一定是起点到该点合法的最短路

其余细节:1.压维好写一点

                  2.答案要对2的64次方取模,可以利用unsigned long long 自然溢出,也可以用__int128 

ac代码如下(有点冗余):

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
//#define int long long
#define double long double
#define pii pair<int,int>
#define se second
#define fi first
using namespace std;
typedef unsigned long long ULL;
const int N=1e7+10,INF=4e18;
const double eps=1e-15;

char g[N];
int d[N],dw[N],st[N];
int m,n,k,root,cnt;

int vx[]={1,-1,0,0};
int vy[]={0,0,-1,1};

int get(int a,int b)
{
    return a*3010+b;
}

pii fget(int a)
{
    int x=a/3010;
    int y=a%3010;
    return {x,y};
}

void bfs()
{
    priority_queue<pii,vector<pii>,greater<pii>> pr;
    d[root]=0;
    pr.push({0,root});
    
    while(pr.size())
    {
        auto t=pr.top();
        pr.pop();
        
        int var = t.se, dis=t.fi;
        if(st[var]) continue;
        st[var]=1;
        
        if((--cnt)==0) break;
        
        pii now=fget(var);
        
        for(int i=0;i<4;i++)
        {
            int x=now.fi+vx[i];
            int y=now.se+vy[i];
            if(x<=0||x>m||y<=0||y>n) continue;
            if(g[get(x,y)]=='#') continue;
            int dd;
            if(dis+1>dw[get(x,y)]) 
                dd=dis+1;
            else dd=dw[get(x,y)]+1;
            
            if(d[get(x,y)]>dd) 
            {
                d[get(x,y)]=dd;
                pr.push({dd,get(x,y)});
            }    
        }
    }
}

void solve()
{
    cin>>m>>n>>k;
    
    for(int i=1;i<=k;i++)
    {
        int a,b;
        cin>>a>>b;
        dw[get(a,b)]=k-i;
        if(i==1) root=get(a,b);
    }
    
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++){
            d[get(i,j)]=0x3f3f3f3f;
            cin>>g[get(i,j)];
            if(g[get(i,j)]=='.') cnt++;
        }
    bfs();
    
    ULL res=0;
    
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        {
            if(d[get(i,j)]!=0x3f3f3f3f)
                res+=(long long)d[get(i,j)]*(long long)d[get(i,j)];    
        }
    cout<<res<<'\n';
}
signed main(){
    int T;
    IOS;
    T=1;
    while(T--)
        solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值