[JOI 2017 Final]足球

一、题目

点此看题

二、解法

把每一个位置拆成 6 6 6个点: 0 − 3 0-3 03分别代表球正在向上下左右四个方向滚动, 4 4 4代表球在该点但是没人持球, 5 5 5代表球在该点而且有人持球。下面详细讲一下建边的方法:

  • 没人持球到有人持球,召唤离当前点最近的 工具 人,这人不用考虑移动,因为一个人持球并踢开后就不可能再持球了,找这个最近的人就可以用 b f s bfs bfs
  • 某个方向到无人持球,边权为 0 0 0
  • 某个方向继续滚动,边权为 A A A
  • 有人持球到下一个位置继续持球,边权为 C C C
  • 有人持球到踢出去(状态还是在当前点),边权为 B B B
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 505;
const int M = 260005;
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
int read()
{
    int num=0,flag=1;
    char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
    return num*flag;
}
int n,m,k,t,A,B,C,tot,f[6*M],dis[6*M],x[M],y[M],vis[N][N];
int dx[5]= {1,-1},dy[5]= {0,0,1,-1};
struct edge
{
    int v,c,next;
} e[20*M];
struct node
{
    int u,c;
    bool operator < (const node &b) const
    {
        return c>b.c;
    }
};
void bfs()
{
    queue<pii> q;
    memset(vis,-1,sizeof vis);
    for(int i=1; i<=k; i++)
    {
        x[i]=read();
        y[i]=read();
        vis[x[i]][y[i]]=0;
        q.push(make_pair(x[i],y[i]));
    }
    while(!q.empty())
    {
        pii t=q.front();
        q.pop();
        for(int i=0; i<4; i++)
        {
            int x=t.fi+dx[i],y=t.se+dy[i];
            if(x>=0 && x<=n && y>=0 && y<=m && vis[x][y]==-1)
            {
                vis[x][y]=vis[t.fi][t.se]+1;
                q.push(make_pair(x,y));
            }
        }
    }
}
int id(int x,int y)
{
    return x*(m+1)+y;
}
void add(int u,int v,int c)
{
    e[++tot]=edge{v,c,f[u]},f[u]=tot;
}
void build()
{
    t=(n+1)*(m+1);
    for(int i=0; i<=n; i++)
        for(int j=0; j<=m; j++)
        {
            add(id(i,j)+4*t,id(i,j)+5*t,C*vis[i][j]);
            for(int k=0; k<4; k++)
            {
                int x=i+dx[k],y=j+dy[k];
                add(id(i,j)+k*t,id(i,j)+4*t,0);
                add(id(i,j)+5*t,id(i,j)+k*t,B);
                if(!(x>=0 && x<=n && y>=0 && y<=m)) continue;
                add(id(i,j)+k*t,id(x,y)+k*t,A);
                add(id(i,j)+5*t,id(x,y)+5*t,C);
            }
        }
}
void dijk()
{
    memset(dis,0x3f,sizeof dis);
    priority_queue<node> q;
    q.push(node{id(x[1],y[1])+5*t,0});
    dis[id(x[1],y[1])+5*t]=0;
    while(!q.empty())
    {
        node t=q.top();
        q.pop();
        if(t.c>dis[t.u]) continue;
        for(int i=f[t.u]; i; i=e[i].next)
        {
            int v=e[i].v,c=e[i].c;
            if(dis[v]>dis[t.u]+c)
            {
                dis[v]=dis[t.u]+c;
                q.push(node{v,dis[v]});
            }
        }
    }
    printf("%lld\n",dis[id(x[k],y[k])+5*t]);
}
signed main()
{
    n=read();
    m=read();
    A=read();
    B=read();
    C=read();
    k=read();
    bfs();
    build();
    dijk();
}

Joi 是一个用于 JavaScript 的强大的对象模型验证库。它可以用来验证和转换复杂的数据结构,如表单输入、API 请求和配置文件。Joi 提供了一组强大的验证规则和函数,可以轻松定义和应用对输入数据的验证逻辑。 Joi 的主要特点包括: 1. 可以通过链式调用来定义验证规则,使代码更加清晰和易读。 2. 支持各种类型的验证,包括字符串、数字、日期、枚举、数组、对象等。 3. 支持自定义验证规则和错误消息。 4. 提供丰富的验证函数,如必需字段、字符串长度、正则表达式匹配、数值范围、枚举值等。 5. 支持异步验证和自动转换。 6. 可以通过 `.validate()` 方法对数据进行验证,并返回验证结果。 以下是一个使用 Joi 进行表单验证的示例: ```javascript const Joi = require('joi'); // 定义验证规则 const schema = Joi.object({ username: Joi.string().alphanum().min(3).max(30).required(), password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).required(), email: Joi.string().email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } }).required(), age: Joi.number().integer().min(18).max(99).required(), }); // 准备待验证的数据 const data = { username: 'john123', password: 'Password123', email: 'john@example.com', age: 25, }; // 进行验证 const result = schema.validate(data); // 输出验证结果 if (result.error) { console.log(result.error.details); } else { console.log('Validation passed'); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值