火星探险问题

题目描述

题解:

将所有点拆开。

$0$点的拆点之间费用为$0$;

$2$点的拆点之间费用为$-1$。

所有点不能到$1$上。

然后最大费用流。

代码:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 3250
const int inf = 0x3f3f3f3f;
inline int rd()
{
    int f=1,c=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();}
    return f*c;
}
int n,p,q,hed[N],cnt=-1,S,T;
int mp[40][40];
int _id(int x,int y)
{
    return (x-1)*p+y;
}
int get_y(int u)
{
    int ret = u%p;
    if(!ret)ret+=p;
    return ret;
}
struct EG
{
    int to,nxt,w,c;
}e[N*200];
void ae(int f,int t,int w,int c)
{
    e[++cnt].to = t;
    e[cnt].nxt = hed[f];
    e[cnt].w = w;
    e[cnt].c = c;
    hed[f] = cnt;
}
int dep[N],fl[N];
int pre[N],fa[N];
bool vis[N];
queue<int>que;
bool spfa()
{
    memset(dep,0x3f,sizeof(dep));
    dep[S]=0,fl[S]=inf,vis[S]=1;que.push(S);
    while(!que.empty())
    {
        int u = que.front();
        que.pop();
        for(int j=hed[u];~j;j=e[j].nxt)
        {
            int to = e[j].to;
            if(e[j].w&&dep[to]>dep[u]+e[j].c)
            {
                dep[to] = dep[u]+e[j].c;
                fl[to] = min(fl[u],e[j].w);
                pre[to] = j,fa[to] = u;
                if(!vis[to])
                {
                    vis[to] = 1;
                    que.push(to);
                }
            }
        }
        vis[u] = 0;
    }
    return dep[T]!=inf;
}
int sta[N],tl;
void mcmf()
{
    while(spfa())
    {
        int u = T;
        while(u!=S)
        {
            e[pre[u]].w-=fl[T];
            e[pre[u]^1].w+=fl[T];
            u = fa[u];
        }
    }
}
void dfs(int u,int c)
{
    for(int j=hed[u];~j;j=e[j].nxt)
    {
        int to = e[j].to;
        if(to==S||to==T)continue;
        if((to>>1)<=(u>>1))continue;
        if(e[j].w==inf)continue;
        e[j].w++;
        printf("%d %d\n",c,get_y(to>>1)!=get_y(u>>1));
        dfs(to+1,c);
        return ;
    }
}
void print()
{
    for(int i=1;i<=n;i++)
        dfs(3,i);
}
int main()
{
    n = rd(),p = rd(),q = rd();
    S = 0,T = 1;
    memset(hed,-1,sizeof(hed));
    for(int i=1;i<=q;i++)
        for(int j=1;j<=p;j++)
            mp[i][j]=rd();
    ae(S,2,n,0),ae(2,S,0,0);
    ae(p*q*2+1,T,n,0),ae(T,p*q*2+1,0,0);
    for(int i=1;i<=q;i++)
        for(int j=1;j<=p;j++)
        {
            if(mp[i][j]==1)continue;
            int u = _id(i,j);
            if(i!=q)
            {
                if(mp[i+1][j]!=1)
                {
                    int to = _id(i+1,j);
                    ae(u<<1|1,to<<1,inf,0);
                    ae(to<<1,u<<1|1,0,0);
                }
            }
            if(j!=p)
            {
                if(mp[i][j+1]!=1)
                {
                    int to = _id(i,j+1);
                    ae(u<<1|1,to<<1,inf,0);
                    ae(to<<1,u<<1|1,0,0);
                }
            }
            if(mp[i][j]==2)
            {
                ae(u<<1,u<<1|1,1,-1);
                ae(u<<1|1,u<<1,0,1);
            }
            ae(u<<1,u<<1|1,inf,0);
            ae(u<<1|1,u<<1,0,0);
        }
    mcmf();
    print();
    return 0;
}

 

转载于:https://www.cnblogs.com/LiGuanlin1124/p/10256511.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值