• 洛谷P2566 [SCOI2009]围豆豆(状压dp+spfa)


    题目传送门

    题解 Σ(っ °Д °;)っ

    前置知识

    射线法:从一点向右(其实哪边都行)水平引一条射线,若射线与路径的交点为偶数,则点不被包含,若为奇数,则被包含。(但注意存在射线与路径重合的情况)
    这里是一篇专门介绍此法的博客:射线法

    思路

    (这次的博客有点粗糙,有空我再加点解释注释啥的)
    数据很小,我们直接暴力之。但情况很多,一一枚举不现实,故状压之:以一个二进制串代表各个豆豆的获得情况(1即获得,0即不获得)。这就是我们的状态(即代码里的condition)。我们把所有(所有起点,所有状态)的状态答案都暴力出来,再根据题意找最优即可。
    ps:不要吐槽我的变量名为啥这么长,我这也是为了尽量通俗易懂一点嘛。
    代码:

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int maxn=11,maxm=1<<11;
    #define ll long long
    ll f[maxn][maxn][maxm],a[maxn],val[maxm];//val[i]中的i是状态
    ll ans=-99999999;
    bool vis[maxn][maxn][maxm];
    int n,m,d,x[maxn],y[maxn],num[maxn][maxn];//x[],y[],分别储存了豆豆的坐标
    int dx[5]={0,1,-1,0,0},dy[5]={0,0,0,1,-1};//第一位数dx[0],dy[0]是无意义的
    struct node{
        int started,ended,condition;
        node();
        node(int x,int y,int z){
            started=x,ended=y,condition=z;
        }
    };
    int Change(int last_x,int last_y,int next_x,int next_y,int last_condition){
        int next_conditon=last_condition;//下一步状态由上一步转换而来
        for(int i=1;i<=d;i++){//枚举豆豆
            //判断是是否上下运动,为了避免上面所说的重合情况,只有上下运动我们才更新状态
            if(((last_x==x[i]&&next_x<x[i])||(last_x<x[i]&&next_x==x[i]))&&next_y>y[i])//我们走一步可能让一个豆豆进圈或者出圈
                next_conditon=next_conditon^(1<<(i-1));//把二进制的第i位(从右到左哦)取反
        }
        return next_conditon;
    }
    queue<node> q;
    void Spfa(int fx,int fy){//我们找出每种状态下的最短路,再枚举判断最优答案
        memset(f,0x3f,sizeof(f));//和普通的spfa一样一样的
        memset(vis,0,sizeof(vis));
        f[fx][fy][0]=0;
        q.push(node(fx,fy,0));
        while(!q.empty()){
            node t=q.front();q.pop();
            int u=t.started,v=t.ended,last_condition=t.condition;
            vis[u][v][last_condition]=0;
            for(int k=1;k<=4;k++){//向四周遍历
                int xx=u+dx[k],yy=v+dy[k];
                if(xx<=0||yy<=0||xx>n||yy>m||num[xx][yy]!=0) continue;//判断越不越界以及点能不能走
                int next_condition;
                next_condition=Change(u,v,xx,yy,last_condition);//更新状态
                if(f[u][v][last_condition]+1<f[xx][yy][next_condition]){//更新最短路
                    f[xx][yy][next_condition]=f[u][v][last_condition]+1;
                    if(vis[xx][yy][next_condition]==0){
                        vis[xx][yy][next_condition]=1;
                        q.push(node(xx,yy,next_condition));
                    }
                }
            }
        }
        for(int i=0;i<(1<<d);i++){//枚举状态找最优答案
            ans=max(ans,val[i]-f[fx][fy][i]);
        }
    }
    int main(){
        cin>>n>>m>>d;
        for(int i=1;i<=d;i++){
            scanf("%lld",&a[i]);
        }
        char ch;
        for(int i=0;i<(1<<d);i++)//预处理出每种状态下的总价值val[i](i是状态哦)
        for(int j=1;j<=d;j++)
            if(i&(1<<(j-1))) val[i]+=a[j];//如果状态中存在j号豆豆
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf(" %c",&ch);
            if(ch=='#') num[i][j]=-1;
            else if(ch<='9'&&ch>='0'){
                x[ch-'0']=i,y[ch-'0']=j,num[i][j]=ch-'0';//num[i]存储每个点的数据
            }
        }
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(!num[i][j]) Spfa(i,j);//因为要围豆豆,当然不能从豆豆出发,障碍物也不行
        cout<<ans<<endl;
        return 0;
    }
    

    还有一种更简单一点的写法:

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #include<iostream>
    using namespace std;
    const int maxn=11,maxm=1001;
    #define ll long long
    int n,m,d,cross;
    int dx[5]={0,1,-1,0,0},dy[5]={0,0,0,1,-1};//第一位数dx[0],dy[0]是无意义的
    ll f[maxn][maxn][maxm],val[maxn],vis[maxn][maxn],in[maxn][maxn][maxm],x[maxn],y[maxn];
    struct node{
        int started,ended,condition;//分别存储起点,终点,状态
        node();
        node(int x,int y,int z){
            started=x,ended=y,condition=z;
        } 
    };
    queue<node> q;
    ll now_condition,now_change,ans=0;
    void Check(int fx,int fy){
        memset(f,0,sizeof(f));
        memset(in,0,sizeof(in));
        f[fx][fy][0]=0;
        q.push(node(fx,fy,0));
        while(!q.empty()){
            node t=q.front();q.pop();
            int u=t.started,v=t.ended,w=t.condition;
            for(int k=1;k<=4;k++){
                int xx=u+dx[k],yy=v+dy[k];
                if(xx<=0||yy<=0||xx>n||yy>m||vis[xx][yy]!=0) continue;
                now_condition=w;
                now_change=f[u][v][w]-1;
                if(k<=2){
                    cross=min(xx,u);
                    for(int i=1;i<=d;i++){
                        if(x[i]!=cross||y[i]>yy) continue;
                        if(now_condition>>(i-1)&1){
                            now_change-=val[i];
                            //cout<<now_condition<<endl;
                        }
                        else  now_change+=val[i];
                        now_condition=now_condition^(1<<(i-1));
                    }
                }
                if(!in[xx][yy][now_condition]++) f[xx][yy][now_condition]=now_change,q.push(node(xx,yy,now_condition));
            }
        }
        for(int i=0;i<(1<<d);i++){
            if(in[fx][fy][i]) ans=max(ans,f[fx][fy][i]);
        }
    }
    int main(){
        cin>>n>>m>>d;
        for(int i=1;i<=d;i++){
            scanf("%lld",&val[i]);
        }
        char ch;
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf(" %c",&ch);
            if(ch=='#') vis[i][j]=-1;
            else if(ch<='9'&&ch>='0'){
                x[ch-'0']=i,y[ch-'0']=j,vis[i][j]=ch-'0';
            }
        }
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(!vis[i][j]) Check(i,j);
        cout<<ans<<endl;
        return 0;
    }
    
  • 相关阅读:
    [leetcode]259. 3Sum Smaller 三数之和小于目标值
    题型总结之K Sum
    [Leetcode]167. Two Sum II
    题型总结之Sliding Window
    [Leetcode]703. Kth Largest Element in a Stream 数据流中的第 K 大元素
    [Leetcode]307. Range Sum Query
    pycharm同一目录下无法import明明已经存在的.py文件
    python高级特性:迭代器与生成器
    self的含义,为什么类调用方法时需要传参数?
    git三:远程仓库GitHub
  • 原文地址:https://www.cnblogs.com/Zfio/p/12749653.html
Copyright © 2020-2023  润新知