[caioj]1492: 基于连通性状态压缩的 动态规划问题:Pipes

9 篇文章 0 订阅

【题目描述】
给你一个 n * m 的地图,地图上的所有格子都是可以走的,每个格子之间都用墙壁分开,穿过一道墙需要一定的费用,求用一条回路经过所有格子的最小费用,每个格子只能经过一次。

题解:

和入门题差不多,只要把方案数改为最小花费来DP就行了。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
const int mod=100037;
const int Up=0;
const int Down=1;
const int Left=2;
const int Right=3;
int tx[]={-1,1,0,0};
int ty[]={0,0,-1,1};
int n,m,map[12][12],xx,yy,now,list[2][mod],num[2];
//                            状态列表    状态总数 
LL state[2][mod],ans,val[12][12][12][12];
bool pd(int x,int y,int k)
{
    int xx=x+tx[k],yy=y+ty[k];
    return val[x][y][xx][yy]!=-1;
}
int HASH[mod];
LL get(int s,int p)//获取状态s从右向左数的第p位 
{return (s>>((p-1)*2))&3;}
void change(int &s,int p,int v)//把状态s的第p位改成v
{
    s^=get(s,p)<<((p-1)*2);
    s^=(v)<<((p-1)*2);
}
void update(LL &x,LL y){/*printf("%lld %lld\n",x,y);*/x=min(x,y);}
void add(int now,int st,LL sum)
{
    int ss=st%mod;
    while(list[now][HASH[ss]]!=st&&HASH[ss]!=-1)
    {
        ss++;ss%=mod;
        if(ss==0)ss=1;
    }
    if(HASH[ss]==-1)
    {
        HASH[ss]=++num[now];
        list[now][num[now]]=st;
        state[now][num[now]]=sum;
    }
    else update(state[now][HASH[ss]],sum);
}
void work()
{
    state[0][1]=0;num[0]=1;list[0][1]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            now^=1;
            num[now]=0;
            memset(HASH,-1,sizeof(HASH));
            for(int k=1;k<=num[now^1];k++)
            {
                int st=list[now^1][k];//获取上次的状态 
                LL sum=state[now^1][k];//获取此状态的花费 
                int p=get(st,j);//获取上次的两个插头 
                int q=get(st,j+1);
                if(!p&&!q)//没有连到当前的格子 
                {
                    if(map[i][j+1]&&map[i+1][j]&&pd(i,j,Down)&&pd(i,j,Right))
                    {
                        change(st,j,1);
                        change(st,j+1,2);
                        add(now,st,sum+val[i][j][i][j+1]+val[i][j][i+1][j]);
                    }
                }
                else if(!p&&q)
                {
                    if(map[i][j+1]&&pd(i,j,Right))add(now,st,sum+val[i][j][i][j+1]);
                    if(map[i+1][j]&&pd(i,j,Down))
                    {
                        change(st,j,q);
                        change(st,j+1,0);
                        add(now,st,sum+val[i][j][i+1][j]);
                    }
                }
                else if(p&&!q)
                {
                    if(map[i+1][j]&&pd(i,j,Down))add(now,st,sum+val[i][j][i+1][j]);
                    if(map[i][j+1]&&pd(i,j,Right))
                    {
                        change(st,j,0);
                        change(st,j+1,p);
                        add(now,st,sum+val[i][j][i][j+1]);
                    }
                }
                else if(p==1&&q==2)
                {
                    if(i==xx&&j==yy)update(ans,sum);
                }
                else if(p==2&&q==1)
                {
                    change(st,j,0);
                    change(st,j+1,0);
                    add(now,st,sum);
                }
                else if(p==1&&q==1)
                {
                    int top=1;
                    for(int pos=j+2;pos<=m+1;pos++)
                    {
                        int temp=get(st,pos);
                        if(temp==1)top++;
                        if(temp==2)top--;
                        if(top==0)
                        {
                            change(st,j,0);
                            change(st,j+1,0);
                            change(st,pos,1);
                            add(now,st,sum);
                            break;
                        }
                    }
                }
                else if(p==2&&q==2)
                {
                    int top=1;
                    for(int pos=j-1;pos;pos--)
                    {
                        int temp=get(st,pos);
                        if(temp==2)top++;
                        if(temp==1)top--;
                        if(top==0)
                        {
                            change(st,j,0);
                            change(st,j+1,0);
                            change(st,pos,2);
                            add(now,st,sum);
                            break;
                        }
                    }
                }
            }
        }
        for(int j=1;j<=num[now];j++)list[now][j]<<=2;
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(val,-1,sizeof(val));
        memset(map,0,sizeof(map));
        now=0;ans=(1LL<<60);
        scanf("%d%d\n",&n,&m);
        xx=n;yy=m;
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        map[i][j]=1;
        for(int i=1;i<=2*n+1;i++)
        {
            char str[30];
            gets(str+1);
            if(i==1||i==2*n+1)continue;
            if(!(i&1))
            {
                for(int j=3;j<=2*m-1;j+=2)
                {
                    if(str[j]>='0'&&str[j]<='9')
                    val[i>>1][(j-1)>>1][i>>1][(j+1)>>1]=(LL)(str[j]-'0');
                }
            }
            else
            {
                for(int j=2;j<=2*m;j+=2)
                {
                    if(str[j]>='0'&&str[j]<='9')
                    val[i>>1][j>>1][(i>>1)+1][j>>1]=(LL)(str[j]-'0');
                }
            }
        }
        work();
        printf("%lld\n",ans);
    }
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值