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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值