bzoj千题计划230:bzoj3205: [Apio2013]机器人

http://www.lydsy.com/JudgeOnline/problem.php?id=3205

历时一天,老子终于把它A了

哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈

因为不懂spfa的优化 以及 数组越界  TAT

┭┮﹏┭┮

牢骚发完了,题解在下面  (⊙o⊙)…

n只有9,很像状压dp

dp[l][r][x][y] 表示在(x,y)位置 合成了x-y复合机器人 的最少推动次数

它的转移 存在后效性

所以上 斯坦纳树

自身的转移:dp[l][r][x][y]=min{dp[l][k][x][y]+dp[k+1][r][x][y]}

互相转移:

预处理出 在(x,y)位置 向4个方向推,最终会停留在哪个位置

可以记忆化搜索

然后用spfa

裸的spfa 得了65

加上SLF优化得了75

再加LLL优化还是75,但慢了一点儿 (可能是我不会用吧TAT)

然后改了双端队列还是75

最后参考了一位大佬的做法:

双端队列优化只是考虑 当前要入队的和在队首的 目前哪个更优

那么可以将这个扩展成单调队列,根noip2017蚯蚓很像

两个队列q1,q2

q2中存放在自身转移中入队的状态,从小到大排好序

然后由q2中转移出的状态,全部放进q1,

因为转移代价是1,q2又单调,所以可以保证q1也是单调的

每次比较q1和q2的队首,取出目前更优的状态 扩展新的状态

#include<queue>
#include<cstdio>
#include<cstring>

using namespace std;

#define N 501

typedef pair<int,int> pii;

#define MP(x,y) make_pair(x,y)

int T,m,n;
char s[N];

int map[N][N];

int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};

int pos[4][N][N];
bool v[4][N][N];

int dp[10][10][N][N];

int tot,mx;
int cnt[N*N*3];
pii tmp[N*N],q[N*N];
queue<pii>q1,q2;

bool vis[N][N];

inline int &min(int &x,int &y) { return x<y ? x: y; }

bool inmap(int x,int y)
{
    if(x<=0 || x>n || y<=0 || y>m) return false;
    if(!map[x][y]) return false;
    return true;
}

int find(int x,int y,int d)
{
    if(pos[d][x][y]) return pos[d][x][y];
    if(!map[x][y]) return -2;
    if(v[d][x][y]) return -1;
    v[d][x][y]=true;
    int nd=d;
    if(map[x][y]==-1) nd=(d-1+4)%4;
    if(map[x][y]==-2) nd=(d+1)%4;
    int npos=-1;
    if(!inmap(x+dx[nd],y+dy[nd])) npos=-2;
    else npos=find(x+dx[nd],y+dy[nd],nd);
    v[d][x][y]=false;
    if(npos==-2) return pos[d][x][y]=(x-1)*m+y;
    else return pos[d][x][y]=npos;
}

void spfa(int l,int r)
{
      for(int i=0;i<=mx;++i) cnt[i]=0;
    for(int i=1;i<=tot;++i) cnt[dp[l][r][tmp[i].first][tmp[i].second]]++;
    for(int i=1;i<=mx;++i) cnt[i]+=cnt[i-1];
    for(int i=tot;i;--i) q[cnt[dp[l][r][tmp[i].first][tmp[i].second]]--]=tmp[i];
    for(int i=1;i<=tot;++i) q2.push(q[i]);
    int x,y,nx,ny;
    while(!q2.empty() || !q1.empty())
    {
        if(q1.empty())
        {
            x=q2.front().first;
            y=q2.front().second;
            q2.pop();
        }
        else if(q2.empty())
        {
            x=q1.front().first;
            y=q1.front().second;
            q1.pop();
            vis[x][y]=false;
        }
        else if(dp[l][r][q1.front().first][q1.front().second]>dp[l][r][q2.front().first][q2.front().second])
        {
            x=q2.front().first;
            y=q2.front().second;
            q2.pop();
        }
        else
        {
            x=q1.front().first;
            y=q1.front().second;
            q1.pop();
            vis[x][y]=false;
        }
        for(int i=0;i<4;++i)
        {
            if(pos[i][x][y]==-1) continue;
            nx=(pos[i][x][y]-1)/m+1;
            ny=pos[i][x][y]-(nx-1)*m;
            if(dp[l][r][x][y]+1<dp[l][r][nx][ny])
            {
                dp[l][r][nx][ny]=dp[l][r][x][y]+1;
                if(!vis[nx][ny]) q1.push(MP(nx,ny)),vis[nx][ny]=true;
            }
        }
    }
}

int main()
{
    scanf("%d%d%d",&T,&m,&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s+1);
        for(int j=1;j<=m;++j)
            if(s[j]==‘A‘) map[i][j]=-1;
            else if(s[j]==‘C‘) map[i][j]=-2;
            else if(s[j]>=‘1‘ && s[j]<=‘9‘) map[i][j]=s[j]-‘0‘;
            else if(s[j]==‘.‘) map[i][j]=10;
    }
    for(int d=0;d<4;++d)
    {
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
                if(map[i][j]) find(i,j,d);
    }
    memset(dp,63,sizeof(dp));
    int oo=dp[0][0][0][0];
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if(map[i][j]>=1 && map[i][j]<=9) dp[map[i][j]][map[i][j]][i][j]=0;
    for(int i=T;i;--i)
        for(int j=i;j<=T;++j)
        {
            tot=mx=0;
            for(int x=1;x<=n;++x)
                for(int y=1;y<=m;++y)
                {
                    for(int k=i;k<j;++k)
                        dp[i][j][x][y]=min(dp[i][j][x][y],dp[i][k][x][y]+dp[k+1][j][x][y]);
                    if(dp[i][j][x][y]!=oo) tmp[++tot]=MP(x,y),mx=max(mx,dp[i][j][x][y]);
                }
            if(tot) spfa(i,j);
        }
    int ans=oo;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            ans=min(ans,dp[1][T][i][j]);
    if(ans==oo) printf("-1");
    else printf("%d",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8434181.html

时间: 2024-11-06 07:23:18

bzoj千题计划230:bzoj3205: [Apio2013]机器人的相关文章

bzoj千题计划185:bzoj1260: [CQOI2007]涂色paint

http://www.lydsy.com/JudgeOnline/problem.php?id=1260 区间DP模型 dp[l][r] 表示涂完区间[l,r]所需的最少次数 从小到大们枚举区间[l,r] 如果col[l]==col[r] dp[l][r]=min(dp[l+1][r],dp[l][r-1],dp[l+1][r-1]+1) 否则 dp[l][r]=min(dp[l][k]+dp[k+1][r]) 我还是辣鸡啊~~~~(>_<)~~~~,这种题都不能秒 #include<c

bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹

http://www.lydsy.com/JudgeOnline/problem.php?id=2244 每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数 pre_len [i] 表示以i结尾的最长不下降子序列的长度 pre_sum[i] 表示对应长度下的方案数 suf_len[i] 表示以i开头的最长不下降子序列长度 suf_sum[i] 表示对应长度下的方案数 若已有了这4个数组 设最长上升子序列长度=mx 那么 如果pre_len[i]+suf_len[i] -

bzoj千题计划304:bzoj3676: [Apio2014]回文串

https://www.lydsy.com/JudgeOnline/problem.php?id=3676 回文自动机模板题 4年前的APIO如今竟沦为模板,,,╮(╯▽╰)╭,唉 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 300001 char ss[N]; int s[N]; int tot=1,last; int fail[N],len

bzoj千题计划106:bzoj1014 [JSOI2008]火星人prefix

http://www.lydsy.com/JudgeOnline/problem.php?id=1014 两个后缀的最长公共前缀:二分+hash 带修改带插入:splay维护 #include<cstdio> #include<cstring> #include<iostream> #define L 100001 typedef unsigned long long ULL; using namespace std; char s[L+4]; int tot,root

bzoj千题计划108:bzoj1018: [SHOI2008]堵塞的交通traffic

http://www.lydsy.com/JudgeOnline/problem.php?id=1018 关键点在于只有两行 所以一个2*m矩形连通情况只有6种 编号即对应代码中的a数组 线段树维护 用b数组表示 节点第0/1行的最右一列是否连接了右边 来 辅助 节点的合并 查询 对两个点位于矩形的位置分4种情况讨论 两点是否联通,要考虑四种情况 (以两个位置是矩形左上角和右上角为例) 1.直接联通,线段树的节点包含了这种情况,直接判断 2. 3. 4. 后三种情况需要再查询[1,l]和[r,n

bzoj千题计划109:bzoj1019: [SHOI2008]汉诺塔

http://www.lydsy.com/JudgeOnline/problem.php?id=1019 题目中问步骤数,没说最少 可以大胆猜测移动方案唯一 (真的是唯一但不会证) 设f[i][j] 表示 从i号柱子 上把j个盘子移到 g[i][j] 柱子上的步数 初始化:f[0][1]=1,g[0][1] 根据优先级决定 设三根柱子分别为0,1,2 对于每一个f[x][i], 把前i-1个移走,把第i个移走,把前i-1个移回 令y=g[x][i-1],则k=0+1+2-x-y 我们希望 把i-

bzoj千题计划111:bzoj1021: [SHOI2008]Debt 循环的债务

http://www.lydsy.com/JudgeOnline/problem.php?id=1021 如果A收到了B的1张10元,那么A绝对不会把这张10元再给C 因为这样不如B直接给C优 由此可以推出 若A欠B20元,B欠C 30元, 那么A还C20元,B还C10元最优 所以一共只有 A->BC   B->AC  C->AB AB->C  BC->A  AC->B 这6种转移情况 根据输入,我们可以知道三人最终手中有多少钱ea.eb.ec,一共有多少钱sum 设f

bzoj千题计划112:bzoj1022: [SHOI2008]小约翰的游戏John

http://www.lydsy.com/JudgeOnline/problem.php?id=1022 http://www.cnblogs.com/TheRoadToTheGold/p/6744825.html #include<cstdio> #include<iostream> using namespace std; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar();

bzoj千题计划113:bzoj1023: [SHOI2008]cactus仙人掌图

http://www.lydsy.com/JudgeOnline/problem.php?id=1023 dp[x] 表示以x为端点的最长链 子节点与x不在同一个环上,那就是两条最长半链长度 子节点与x在同一个环上,环形DP,单调队列优化 对于每一个环,深度最小的那个点 有可能会更新 上层节点, 所以 每一个环DP完之后,更新 dp[深度最小的点] #include<cstdio> #include<iostream> #include<algorithm> using