• SCOI2009 围豆豆


    题目链接https://www.luogu.com.cn/problem/P2566

    分析

    这道题看着前边的叙述好像不是很难,不就是把豆子围起来吗,一看数据,(状压DP)肯定是了,状压(DP)的本质其实还是暴力,既然我们已经选择了一个暴力,不如暴力到底,于是我开始枚举围成的路径,打了一堆写好后发现好像不对,(我最开始以为路径一定是矩形),然后.....路径所形成的多边形(可能是含自交的复杂多边形),所以枚举是不现实的了,这里就有一个很神奇的办法,射线法判断点是不是在多边形内,具体看链接::https://blog.csdn.net/qq_27161673/article/details/52973866,一句话总结就是引一条切线,如果与多边形有偶数个交点,则在外部,奇数个则在内部,但你会发现当点在多边形的边上时这个是不成立的,所以我们还需要做点事情,当然根据那个链接里边的做也行,不过好像过于繁琐,我们只需要将这条边上开下闭即可,这样就不会出问题了。转移的时候怎么转移呢?
    因为涉及到每个豆子,并且豆子和豆子都不一样,所以坐标就直接会占二维。状压(DP)的话还要额外再加一层状态,就是每个豆子放与不放,对应的二进制位为1则放否则不放。这样就确定好了状态,下面我们需要跑一条回路出来,我感觉没有比(SPFA)更合适的了,从每一个可以被围起来的点开始跑,一直跑到队列为空,那可能会死循环吗?显然不会,因为跑着跑着步数就变多了,然后就不会转移,然后就不会入队列了,所以不会死循环。
    大概思路是有了,下面考虑一些细节的东西。
    怎么判断是交了奇数次还是偶数次呢?有一个东西叫异或,很好用,之前写NOI ONLINE的一道题时也用过,对于当前状态,假如该路径经过了豆子引出的射线,就把该二进制位异或1,如果它原来是1,就是曾经经过,那么再次经过就应该是偶数次了,异或1后是0,考虑围住的豆子时就应该把这个减去,emm好像还不是很清楚,就是我当前的(DP)数组加上了这个豆子,但是我当前的状态并没有,所以转移的时候一定要把这颗豆子减去,反之则加上,我感觉这么做好像写起来会简单一些。

    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=15,M=12;
    int val[M],dp[N][N][1<<M],sum[1<<M],n,m,num;;
    int g[N][N],ans;
    bool vis[N][N][1<<M];
    struct Node{
        int x,y,zt;
        Node(){}
        Node(int a,int b,int c){
            x=a;y=b;zt=c;
        }
    }bea[M];
    const int dx[4]={0,0,-1,1};
    const int dy[4]={-1,1,0,0};
    void spfa(int sx,int sy){
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        for(int k=0;k<1<<num;k++)
            dp[i][j][k]=-0x3f3f3f3f;
        queue<Node > q;
        q.push(Node(sx,sy,0));
        dp[sx][sy][0]=0;
        vis[sx][sy][0]=1;
        while(!q.empty()){
            Node u=q.front();q.pop();
            vis[u.x][u.y][u.zt]=0;
            if(u.x==sx&&u.y==sy)
                ans=max(ans,dp[u.x][u.y][u.zt]);
            for(int k=0;k<4;k++){
                int x=u.x+dx[k],y=u.y+dy[k];
                int sum=0;
                if (x<=0||x>n||y<=0||y>m||g[x][y]) continue;
                int zt=u.zt,yy=max(y,u.y);
                if(k<=1)
                    for(int i=1;i<=num;i++)
                        if(bea[i].y==yy&&bea[i].x<x){
                            zt^=1<<i-1;
                            if(zt>>i-1&1)sum+=val[i];
                            else sum-=val[i];
                        }
                if(dp[x][y][zt]<dp[u.x][u.y][u.zt]+sum-1){
                    dp[x][y][zt]=dp[u.x][u.y][u.zt]+sum-1;
                    if(!vis[x][y][zt])vis[x][y][zt]=1,q.push(Node(x,y,zt));
                }
            }
        }
    
    }
    int main(){
        scanf("%d%d%d",&n,&m,&num);
        for(int i=1;i<=num;i++)scanf("%d",&val[i]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                char ch=getchar();
                while((ch<'0'||ch>'9')&&ch!='#')ch=getchar();
                if(ch=='#')g[i][j]=-1;
                else g[i][j]=ch-'0';
                if(g[i][j]>=1)bea[g[i][j]].x=i,bea[g[i][j]].y=j;
            }
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
                if(!g[i][j])spfa(i,j);
        printf("%d
    ",ans);
    }
    
  • 相关阅读:
    set bootarges
    UI 中的 结构体 字符串的 初始化
    putchar 代替printf
    石家庄 工作
    What's the value of i++ + i++?
    printf 打印 指定长度 字符串
    UI 点滴 积累
    static 关键字
    sdk
    隐式类型转换
  • 原文地址:https://www.cnblogs.com/anyixing-fly/p/12702843.html
Copyright © 2020-2023  润新知