BZOJ 3205 Apio2013 机器人 斯坦纳树

题目大意:给定一张地图,一些地方有障碍物,有k<=9个机器人,可以一推到底,遇到转向器会转向,两个编号相邻的机器人可以合并,求最少推多少次可以全部合并

令f[l][r][i][j]表示在点(i,j)将编号在[l,r]区间内的机器人全部合并的最小推动次数

则有动规方程组:

f[l][r][i][j]=min{f[l][r][_i][_j]+1} ( (_i,_j)->(i,j) )

f[l][r][i][j]=min(f[l][temp][i][j]+f[temp+1][r][i][j]) (l<=temp<r)

我们首先用记忆化搜索处理出每个点向四个方向推动后会到哪

然后利用根据这两个方程跑斯坦纳树即可

下面是细节部分:

1.转向器有环 因此会无限递归爆系统栈 标记一下就好

2.处理上面那个之后本机测还是会爆系统栈 没事交上去就不爆了

3.SPFA有一个优化 不加会T

观察这个图 发现所有边的边权都是1 如果是单源的话SPFA可以进化成广搜

现在是多源 因此我们可以这样做:

维护两个队列,将初始所有的点按照距离排序后从小到大加入队列1

每次拓展出的点加入队列2

每次取出点的时候,如果队列1队尾元素的距离小于队列2 就把队列1的队尾元素拿去松弛 否则就用队列2的

这样做之后除了排序之外复杂度是线性的

排序的log可以用计数排序省掉,但是直接sort也能过,无妨

然后这题就搞掉了。。。。。。

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 510
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int> abcd;
template<typename T>class Reader{
private:
    T memory[M][M];
public:
    T& operator [] (const abcd &x)
    {
        return memory[x.first][x.second];
    }
    T* operator [] (int x)
    {
        return memory[x];
    }
};
int m,n,k,T;
Reader<char> map;
Reader<int> f[10][10];
//f[l][r][i][j]表示编号为[l,r]的机器人在(i,j)上的最小花销
Reader<abcd[4]> aim;
Reader<int[4]> mark;
//0-上 1-左 2-下 3-右
//A +1 %4
//C +3 %4
queue<abcd> q1,q2;
Reader<bool> v;
abcd pos[10];
abcd stack[M*M];int top;
int L,R;
bool Compare(const abcd &x,const abcd &y)
{
    return f[L][R][x] > f[L][R][y] ;
}
bool operator ! (const abcd &x)
{
    return x.first==0 && x.second==0 ;
}
abcd Memorial_Search(int x,int y,int dir)
{
    static const int dx[]={-1,0,1,0};
    static const int dy[]={0,-1,0,1};
    static int xx,yy;

    if(mark[x][y][dir]==T)
        return abcd(-1,-1);
    mark[x][y][dir]=T;

    if(!!aim[x][y][dir])
        return aim[x][y][dir];
    abcd& re=aim[x][y][dir];

    if(map[x][y]=='A')
        dir=(dir+1)%4;
    if(map[x][y]=='C')
        dir=(dir+3)%4;
    xx=x+dx[dir];yy=y+dy[dir];
    if(xx<=0||yy<=0||xx>m||yy>n||map[xx][yy]=='x')
        return re=abcd(x,y);
    return re=Memorial_Search(xx,yy,dir);
}
void SPFA(int l,int r)
{
    int dir;
    abcd x;
    L=l;R=r;sort(stack+1,stack+top+1,Compare);
    while(top)
        q1.push(stack[top--]);
    while( !q1.empty() || !q2.empty() )
    {
        if( q1.empty() )
            x=q2.front(),q2.pop();
        else if( q2.empty() )
            x=q1.front(),q1.pop();
        else if( f[l][r][q1.front()] < f[l][r][q2.front()] )
            x=q1.front(),q1.pop();
        else
            x=q2.front(),q2.pop();
        v[x]=0;
        for(dir=0;dir<4;dir++)
        {
            abcd y=aim[x.first][x.second][dir];
            if(y.first==-1&&y.second==-1)
                continue;
            if(f[l][r][y]>f[l][r][x]+1)
            {
                f[l][r][y]=f[l][r][x]+1;
                if(!v[y])
                    v[y]=1,q2.push(y);
            }
        }
    }
}
void Steiner_Tree()
{
    int l,r,len,i,j,temp;
    for(i=1;i<=k;i++)
    {
        stack[++top]=pos[i];
        SPFA(i,i);
    }
    for(len=2;len<=k;len++)
        for(l=1;(r=l+len-1)<=k;l++)
        {
            for(i=1;i<=m;i++)
                for(j=1;j<=n;j++)
                {
                    for(temp=l;temp<r;temp++)
                        f[l][r][i][j]=min(f[l][r][i][j],f[l][temp][i][j]+f[temp+1][r][i][j]);
                    if(f[l][r][i][j]!=INF)
                    {
                        abcd temp(i,j);
                        stack[++top]=temp;
                        v[temp]=1;
                    }
                }
            SPFA(l,r);
        }
}
int main()
{

    #ifndef ONLINE_JUDGE
    freopen("robot.in","r",stdin);
    freopen("robot.out","w",stdout);
    #endif

    int i,j,dir;
    cin>>k>>n>>m;
    memset(&f,0x3f,sizeof f);
    for(i=1;i<=m;i++)
    {
        static char s[M];
        scanf("%s",s+1);
        for(j=1;j<=n;j++)
        {
            map[i][j]=s[j];
            if( map[i][j]>'0' && map[i][j]<='9' )
            {
                int temp=map[i][j]-'0';
                f[temp][temp][i][j]=0;
                pos[temp]=abcd(i,j);
            }
        }
    }
    for(i=1;i<=m;i++)
        for(j=1;j<=n;j++)
            if(map[i][j]!='x')
                for(dir=0;dir<4;dir++)
                    ++T,Memorial_Search(i,j,dir);
    Steiner_Tree();
    int ans=INF;
    for(i=1;i<=m;i++)
        for(j=1;j<=n;j++)
            ans=min(ans,f[1][k][i][j]);
    if(ans==INF) ans=-1;
    cout<<ans<<endl;
    return 0;
}
时间: 2024-10-29 23:33:15

BZOJ 3205 Apio2013 机器人 斯坦纳树的相关文章

BZOJ 3205 [Apio2013]机器人 ——斯坦纳树

腊鸡题目,实在卡不过去. (改了一下午) 就是裸的斯坦纳树的题目,一方面合并子集,另一方面SPFA迭代求解. 优化了许多地方,甚至基数排序都写了. 还是T到死,不打算改了,就这样吧 #include <map> #include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm

[APIO2013]机器人(斯坦纳树)

题目描述 VRI(Voltron 机器人学会)的工程师建造了 n 个机器人.任意两个兼容的机 器人站在同一个格子时可以合并为一个复合机器人. 我们把机器人用 1 至 n 编号(n ≤ 9).如果两个机器人的编号是连续的,那 么它们是兼容的,可以合并成一个复合机器人.最初这 n 个机器人各自都只有唯 一的编号.而一个由两个或以上的机器人合并构成的复合机器人拥有两个编号, 分别是构成它的所有机器人中最小和最大的编号. 例如,2 号机器人只可以与 1 号或 3 号机器人合并.若 2 号机器人与 3 号

BZOJ 2595 [Wc2008]游览计划 ——斯坦纳树

[题目分析] 斯坦纳树=子集DP+SPFA? 用来学习斯坦纳树的模板. 大概就是用二进制来表示树包含的点,然后用跟几点表示树的形态. 更新分为两种,一种是合并两个子集,一种是换根,换根用SPFA迭代即可. [代码] #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <set> #include <map> #include

【BZOJ 2595】2595: [Wc2008]游览计划 (状压DP+spfa,斯坦纳树?)

2595: [Wc2008]游览计划 Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 1572  Solved: 739 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数目. 相邻的整数用 (若干个) 空格隔开,行首行末也可能有多余的空格. Output 由 N

BZOJ 2595: [Wc2008]游览计划 [DP 状压 斯坦纳树 spfa]【学习笔记】

传送门 题意:略 论文 <SPFA算法的优化及应用> http://www.cnblogs.com/lazycal/p/bzoj-2595.html 本题的核心就是求斯坦纳树: Steiner Tree: Given an undirected graph with non-negative edge weights and a subset of vertices, usually referred to as terminals, the Steiner tree problem in g

BZOJ 2595 Wc2008 游览计划 斯坦纳树

题目大意:给定一个矩阵,有一些关键点,每个格子有权值,选择一些格子使所有关键点连通,求最小权值和 传说中的斯坦纳树- - 感觉不是很难理解的样子 枚举连通的状态,对于每个状态先对每个位置枚举子集进行合并,然后对这个状态的分层图进行SPFA 看了几分代码还是ZKY写的比较简洁- - 此外就是终于能通过操作符重载访问结构体里的三维数组了- - 我真是太丧病了233 #include <cstdio> #include <cstring> #include <iostream>

FJoi2017 1月20日模拟赛 直线斯坦纳树(暴力+最小生成树+骗分+人工构造+随机乱搞)

[题目描述] 给定二维平面上n个整点,求该图的一个直线斯坦纳树,使得树的边长度总和尽量小. 直线斯坦纳树:使所有给定的点连通的树,所有边必须平行于坐标轴,允许在给定点外增加额外的中间节点. 如下图所示为两种直线斯坦纳树的生成方案,蓝色点为给定的点,红色点为中间节点. [输入格式] 第一行一个整数n,表示点数. 以下n行,每行两个整数表示点的x,y坐标. [输出格式] 第一行一个整数m,表示中间节点的个数. 必须满足m <= 10 * n 以下m行,每行2个整数表示中间节点的坐标. 以下n+m-1

HDU 4085 斯坦纳树模板题

Dig The Wells Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 971    Accepted Submission(s): 416 Problem Description You may all know the famous story "Three monks". Recently they find som

【Foreign】修路 [斯坦纳树]

修路 Time Limit: 20 Sec  Memory Limit: 256 MB Description Input Output 仅一行一个整数表示答案. Sample Input 5 5 2 1 3 4 3 5 2 2 3 1 3 4 4 2 4 3 Sample Output 9 HINT Main idea 给定若干对点,选择若干边,询问满足每对点都连通的最小代价. Source 发现 d 非常小,所以我们显然可以使用斯坦纳树来求解. 斯坦纳树是用来解决这种问题的:给定若干关键点,