zoukankan      html  css  js  c++  java
  • 洛谷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;
    }
    
  • 相关阅读:
    OSG-提示“error reading file e:1.jpg file not handled”
    OSG-加载地球文件报0x00000005错误,提示error reading file simple.earth file not handled
    QT-找开工程后,最上方提示the code model could not parse an included file, which might lead to incorrect code completion and highlighting, for example.
    我的书《Unity3D动作游戏开发实战》出版了
    java中无符号类型的第三方库jOOU
    Windows批处理备份mysql数据
    使用 DevTools 时,通用Mapper经常会出现 class x.x.A cannot be cast to x.x.A
    Java版本,Java版本MongoDB驱动,驱动与MongoDB数据库,Spring之间的兼容性
    Jrebel本地激活方法
    wget下载指定网站目录下的所有内容
  • 原文地址:https://www.cnblogs.com/Zfio/p/12749653.html
Copyright © 2011-2022 走看看