sgu 132 Another Chocolate Maniac

装压好题!

首先我们要从两个相邻的格子不能同时为空入手,然后我们不妨枚举每一行最后的状态,那下一行怎么办,我们还需要用一维来记录这一行的进位,也就是这一行用竖的格子放后的状态然后直接枚举即可,看起来像70*(3^14)的复杂度,其实我们对最终状态可以预处理,可以发现最多不超过21个,所以结论是dp[x][f]=dp[y][w]+count[f]+vis[x^f^s^w];

其中count数组记录的是当前二进制位数,vis记录横着放需要多少个,至于s是当前行candle的状态。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#define mp(a,b) make_pair(a,b)
#define xx first
#define yy second
using namespace std;
typedef long long LL;
const int inf=1000000000;
int dp[2][128][128],dp1[128][128];
char c[105][105];
int vis[128];
int cc[128];
vector<int>g;
vector<int>g1[128];
int main()
{
    int i,j,u,n,m;
    cin>>n>>m;
    for(i=1;i<=n;i++){ scanf("%s",c[i]);
        for(j=m;j>=1;j--) c[i][j]=c[i][j-1];
    }
    u=0;
    int p=1<<m;
    for(i=0;i<p;i++){
        vis[i]=inf;
    }
    for(i=1;i<p;i++){
        cc[i]=cc[i^(i^(i&(i-1)))]+1;
    }
    vis[0]=0;
    for(i=1;i<p;i++){
        int w=i&(i-1);
        w=i^w;
        if((i&(w<<1))==0) continue;
        vis[i]=vis[i^w^(w<<1)]+1;
    }
    for(i=0;i<p;i++){
        for(j=0;j<p;j++){
            if((i|j)==i){
                g1[i].push_back(j);
            }
        }
    }
    for(j=0;j<p;j++){
        int q=(p-1)^j;
        int w=((p-1)^j)<<1;
        if((q&w)!=0) continue;
        g.push_back(j);
    }
    for(i=0;i<p;i++){
        for(j=0;j<p;j++){
            dp[u][i][j]=1000000000;
        }
    }
    dp[u][p-1][0]=0;
    for(i=1;i<=n;i++){
        u=u^1;
        int s=0;
        for(j=m;j>=1;j--){
            s=s<<1;
            if(c[i][j]=='*'){
                s+=1;
            }
        }
        for(j=0;j<p;j++){
            int q=g1[j].size();
            for(int f=0;f<q;f++){
                dp1[j][g1[j][f]]=inf;
            }
        }
        int q=g.size();
        for (j=0;j<q;j++) {
            int v=g[j];
            int w=g1[v].size();
            for(int f=0;f<w;f++){
                if(dp[u^1][v][g1[v][f]]!=inf&&(g1[v][f]&s)==0){
                    for(int y1=0;y1<q;y1++){
                        int x=g[y1];
                        if((x|(g1[v][f]|s))!=x) continue;
                        if((((p-1)^v)&((p-1)^x))!=0) continue;
                        int p1=x^((g1[v][f]|s));
                        int ww=g1[p1].size();
                        for(int y=0;y<ww;y++){
                            dp1[x][g1[p1][y]]=min(dp1[x][g1[p1][y]],dp[u^1][v][g1[v][f]]+vis[p1^g1[p1][y]]+cc[g1[p1][y]]);
                        }
                    }
                }
            }
        }
        for(j=0;j<p;j++){
            int q=g1[j].size();
            for(int f=0;f<q;f++){
                dp[u][j][g1[j][f]]=dp1[j][g1[j][f]];
            }
        }
    }
    p=g.size();
    int s=inf;
    for(i=0;i<p;i++){
        s=min(s,dp[u][g[i]][0]);
    }
    cout<<s<<endl;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值