[Bzoj 2547] [Ctsc2002] 玩具兵

2547: [Ctsc2002]玩具兵

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 317  Solved: 152
[Submit][Status][Discuss]

Description

小明的爸爸给他买了一盒玩具兵,其中有 K个步兵,K个骑兵和一个天兵,个个高大威猛,形象逼真。盒子里还有一个M*N棋盘,每个格子(i,j)都有一个高度Hij,并且大得足以容纳所有的玩具兵。小明把所有的玩具兵都放到棋盘上去,突然想到了一种很有趣的玩法:任意挑选T个不同的格子,并给每个格子i规定一个重要值Ri--,游戏的目标就是每次沿东南西北之一的方向把一个玩具兵移动到其相邻的格子中(但不能移动到棋盘外面去),最终使得每个挑选出的格子i上恰好有Ri个玩具兵。小明希望所有的玩具兵都在某个选定的格子中,因此他总是使选出的T个格子的重要值之和等于玩具兵的个数。为了增加难度,小明给玩具兵们的移动方式做了一些规定:

-     步兵只会往高处爬,因此如果两个格子A和B相邻,当且仅当格子A的高度小于或等于B,步兵才可以从A移动到B。

-     骑兵只会往低处跳,因此如果两个格子A和B相邻,当且仅当格子A的高度大于或等于B,骑兵才可以从A移动到B。

-     天兵技术全面,移动不受任何限制。

可是没玩几次,小明就发现这个游戏太难了,他常常玩了好半天也达不到目的。于是,他设计了一种“超能力”,每使用一次超能力的时候,虽然不能移动任何一个玩具兵,但可对它们进行任意多次交换操作,每次交换两个玩具兵。等这次超能力使用完后又可和平常一样继续移动这些玩具兵。借助强大的超能力,这个游戏是容易玩通的,但是怎样才能让使用超能力的次数最少呢?

Input

第一行包含四个整数:M,N,K,T (2<=M,N<=100, 1<=K<=50, 1<=T<=2K+1)

第二行包括2K+1个数对(xi,yi),代表各个玩具兵的初始位置。前K个代表步兵,接下来的K个代表骑兵,最后一个代表天兵。

第三行包含T个三元组(xi,yi,ri),第i组代表第i个目标格的位置和重要值。

以下M行,每行N个整数。其中第i行第j个数为即格子的高度Hij。高度是不超过100的正整数,注意:不同玩具兵的初始位置可能相同。输入数据保证无错,选定的T个格子的重要值之和保证等于2K+1。

Output

仅包含一行,即使用超能力的最小次数T。

Solution

观察到每次交换可以换无数次,所以每次一定是把步兵和骑兵换一下,相当于每个都走到无路可走的地方,然后换个职业继续走。

所以对于每个玩具兵(先不考虑天兵),都处理出到每个点最少要交换的次数,也就是说,至少要交换 _dis[i][j] 次才能到达 (i,j)。

然后二分答案,二分出交换的次数 mid,然后对于每个玩具兵,向交换次数小于 mid 的终点连边,然后跑最大流即可。

注意最后要加上二分出的 mid,意义就是天兵每次都可以替代一个玩具兵走到终点,然后换它们俩就好。

Code

// By YoungNeal
#include<queue>
#include<cstdio>
#include<cstring>

int d[1000005];
int s,t,maxflow;
int head[1000005];
int toy[1005][10];
int goal[1005][10];
int dis[1005][105]; // 记录交换了几次 dis[i][j]->第i个兵到第j个目标
int m,n,k,T,ans,cnt;
int height[105][105];
int _dis[105][105][10];  // _dis[i][j]->从当前的点到(i,j)最少交换次数
bool in[105][105][5]; // in[i][j][p]->当前点到(i,j)职业为p是否在队列
int dx[]={-1,0,1,0},dy[]={0,1,0,-1};

struct Node{
    int x,y,o;
};

struct Edge{
    int to,nxt,flow;
}edge[10000005];

void add(int x,int y,int z){
    edge[++cnt].to=y;
    edge[cnt].nxt=head[x];
    edge[cnt].flow=z;
    head[x]=cnt;
}

void spfa(int now,int xx,int yy,int cnt){
    std::queue<Node> q;
    memset(in,0,sizeof in);
    memset(_dis,0x3f,sizeof _dis);
    _dis[xx][yy][cnt]=0;
    q.push((Node){xx,yy,cnt});
    while(q.size()){
        int x=q.front().x;
        int y=q.front().y;
        int o=q.front().o;
        q.pop(); in[x][y][o]=0;
        for(int k=0;k<4;k++){
            int nx=x+dx[k];
            int ny=y+dy[k];
            int nowdis,oo;
            if(nx<1||nx>m||ny<1||ny>n) continue;
            if((o==0&&height[nx][ny]>=height[x][y])||(o==1&&height[nx][ny]<=height[x][y]))
                nowdis=_dis[x][y][o],oo=o;
            else nowdis=_dis[x][y][o]+1,oo=o^1;
            if(_dis[nx][ny][oo]>nowdis){
                _dis[nx][ny][oo]=nowdis;
                if(!in[nx][ny][oo]) in[nx][ny][oo]=1,q.push((Node){nx,ny,oo});
            }
        }
    }
    for(int i=1;i<=T;i++){
        dis[now][i]=std::min(_dis[goal[i][1]][goal[i][2]][0],_dis[goal[i][1]][goal[i][2]][1]);
    }
}

void clear(){
    maxflow=0;
    cnt=1;s=k*2+T+1;t=s+1;
    memset(edge,0,sizeof edge);
    memset(head,0,sizeof head);
}

bool bfs(){
    std::queue<int> q;
    memset(d,0,sizeof d);
    q.push(s);d[s]=1;
    while(q.size()){
        int u=q.front();q.pop();
        for(int i=head[u];i;i=edge[i].nxt){
            int to=edge[i].to;
            if(d[to]) continue;
            if(!edge[i].flow) continue;
            d[to]=d[u]+1;
            q.push(to);
            if(to==t) return 1;
        }
    }
    return 0;
}

int dinic(int now,int flow){
    if(now==t) return flow;
    int rest=flow,k;
    for(int i=head[now];i;i=edge[i].nxt){
        if(!rest) return flow;
        int to=edge[i].to;
        if(!edge[i].flow) continue;
        if(d[to]!=d[now]+1) continue;
        k=dinic(to,std::min(rest,edge[i].flow));
        if(!k) d[to]=0;
        edge[i].flow-=k;
        edge[i^1].flow+=k;
        rest-=k;
    }
    return flow-rest;
}

bool check(int mid){
    clear();
    for(int i=1;i<=k*2;i++){
        for(int j=1;j<=T;j++){
            if(dis[i][j]<=mid) add(i,j+k*2,1),add(j+k*2,i,0);
        }
    }
    for(int i=1;i<=k*2;i++) add(s,i,1),add(i,s,0);
    for(int i=1;i<=T;i++) add(i+k*2,t,goal[i][3]),add(t,i+k*2,0);
    int flow=0;
    while(bfs()) while(flow=dinic(s,0x3f3f3f3f)) maxflow+=flow;
    return maxflow+mid>=k*2;
}

signed main(){
    scanf("%d%d%d%d",&m,&n,&k,&T);
    for(int i=1;i<=k*2+1;i++) scanf("%d%d",&toy[i][1],&toy[i][2]);
    for(int i=1;i<=T;i++) scanf("%d%d%d",&goal[i][1],&goal[i][2],&goal[i][3]);
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++) scanf("%d",&height[i][j]);
    }
    for(int i=1;i<=(k<<1);i++) spfa(i,toy[i][1],toy[i][2],(i>k));
    int l=0,r=(k<<1)+1;
    while(l<=r){
        int mid=l+r>>1;
        if(check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/YoungNeal/p/8626454.html

时间: 2024-10-07 07:43:03

[Bzoj 2547] [Ctsc2002] 玩具兵的相关文章

bzoj2547: [Ctsc2002]玩具兵

划了一天水,其实我还是有点愧疚的. 传送门 其实是水题,然而我真是太蠢了... 首先不考虑天兵,其他兵要到一个点去一定是通过它-另一种兵-它--这样多次交换的,并且交换对象是无所谓的,和它换的兵最终会是在原位置. 而且骑兵和步兵的数量相等,就不存在一个兵找不到人跟它换的情况,那么一个确定的方案下换的次数最多的兵换的次数就是答案. 先spfa找出每个兵到每个位置的最小换的次数,然后二分答案,把每个兵向它可以到的地方连边,跑最大匹配,考虑到还有天兵,而天兵是可以一步登天的超神存在,所以如果最大匹配数

斜率优化专题2——bzoj 1010 [HNOI2008]玩具装箱toy 题解

[原题] 1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 5434  Solved: 1969 [Submit][Status] Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为1...N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P教授

BZOJ 1010: [HNOI2008]玩具装箱toy [DP 斜率优化]

1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 9812  Solved: 3978[Submit][Status][Discuss] Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为1...N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P

BZOJ 1010 [HNOI2008]玩具装箱toy

1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 7184  Solved: 2724[Submit][Status][Discuss] Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为1...N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P

BZOJ 1054 移动玩具

Description 某人有一套玩具,并想法给玩具命名.首先他选择WING四个字母中的任意一个字母作为玩具的基本名字.然后他会根据自己的喜好,将名字中任意一个字母用“WING”中任意两个字母代替,使得自己的名字能够扩充得很长.现在,他想请你猜猜某一个很长的名字,最初可能是由哪几个字母变形过来的. Input 第一行四个整数W.I.N.G.表示每一个字母能由几种两个字母所替代.接下来W行,每行两个字母,表示W可以用这两个字母替代.接下来I行,每行两个字母,表示I可以用这两个字母替代.接下来N行,

[BZOJ 1010][HNOI2008]玩具装箱toy(斜率优化Dp)

Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压.缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为1...N的N件玩具,第i件玩具经过 压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的.同时如果一个一维容 器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一 个容器中,那么容器的长度将为 x=j-i+Sigma(Ck

bzoj 1010 [HNOI2008]玩具装箱toy(DP的斜率优化)

1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 7874  Solved: 3047[Submit][Status][Discuss] Description P 教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维 容器中.P教授有编号为1...N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理

bzoj 1055 [HAOI2008]玩具取名(区间DP)

1055: [HAOI2008]玩具取名 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1258  Solved: 729[Submit][Status][Discuss] Description 某 人有一套玩具,并想法给玩具命名.首先他选择WING四个字母中的任意一个字母作为玩具的基本名字.然后他会根据自己的喜好,将名字中任意一个字母用 “WING”中任意两个字母代替,使得自己的名字能够扩充得很长.现在,他想请你猜猜某一个很长的名字,最初可能

BZOJ 1010: [HNOI2008]玩具装箱toy 斜率优化DP

1010: [HNOI2008]玩具装箱toy Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为1...N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的.同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一个容器中,那么容器