状态压缩DP专题

poj1038
题意:用2*3格子覆盖有禁区的n*m长方形,问最多可以放置多少个格子。
解法:整行覆盖,第二维存储前两行的覆盖状态
dp[i][st]:前i-1行已覆盖完成,且i-1,i-2行的覆盖状态为st,最多可以放置的格子数
dp[i][st]>dp[i+1][st] 状态转移通过枚举第i行的覆盖状态完成。注意地图上禁区不能覆盖。
状态设计用三进制存储:
0:前两行均无覆盖
1:i-2行覆盖,i-1行无覆盖
2:i-1行覆盖
枚举时从第0列开始,分三种情况:
1.不填充该格
2.横放2*3格子,以该格为右上角
3.竖放2*3格子,以该格为右上角
然后进行dfs,直到枚举到最后一列,得到最终状态st’,进行状态转移即可

# include<iostream>
# include<cstdio>
# include<algorithm>
# include<queue>
# include<complex>
# include<stack>
# include<math.h>
# include<cstring>
#include<map>
#define ll long long
#define pi acos(-1)
#define MP make_pair
#define X first
#define Y second
using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 150+10;
const int maxm = 10+10;
const int maxst = 60000;
int t,n,m;
int dp[3][maxst];
bool ma[maxn][maxm];
int bit[maxm];
int three[maxm],ans,cur;

void init(){
    three[0]=1;
    for(int i=1;i<=11;i++) three[i]=three[i-1]*3;
}

void fenjie(int x,int &y){
    for(int i=0;i<m;i++){
        bit[i]=x%3;
        if(bit[i]!=0)  y+=three[i]*(bit[i]-1);
        x/=3;
    }
}

bool d1(int r,int c){//横放地图限制
    if(ma[r][c]||ma[r][c+1]||ma[r-1][c]||ma[r-1][c+1]||ma[r-2][c]||ma[r-2][c+1]) return false;
    return true;
}

bool d2(int r,int c){//竖放地图限制
    if(ma[r][c]||ma[r-1][c]||ma[r][c+1]||ma[r-1][c+1]||ma[r][c+2]||ma[r-1][c+2]) return false;
    return true;
}

void update(int row,int nowc,int now,int num){
    if(nowc>=m){
        dp[cur][now]=max(dp[cur][now],num);
        return;
    }
    if(row-2>=0&&nowc+1<m&&bit[nowc]==0&&bit[nowc+1]==0&&d1(row,nowc)){//可以横放
            update(row,nowc+2,now+three[nowc]*2+three[nowc+1]*2,num+1);
    }

    if(row-1>=0&&nowc+2<m&&bit[nowc]!=2&&bit[nowc+1]!=2&&bit[nowc+2]!=2&&d2(row,nowc)){//可以竖放
        update(row,nowc+3,now+three[nowc]*2+three[nowc+1]*2+three[nowc+2]*2,num+1);
    }
    update(row,nowc+1,now,num);//不放
}

int main(){
    init();
    //freopen("a.txt","r",stdin);
    scanf("%d",&t);
    while(t--){
        int num;
        scanf("%d%d%d",&n,&m,&num);
        memset(ma,0,sizeof(ma));
        for(int i=1;i<=num;i++){
            int x,y;
            scanf("%d%d",&x,&y); x--,y--;
            ma[x][y]=1;
        }
        //第一列不放
        for(int i=0;i<maxst-3;i++) dp[0][i]=-INF;
        dp[0][0]=0; cur=0;
        //从第二列开始放置
        ans=0;
        for(int i=1;i<n;i++){
            cur^=1;
            for(int j=0;j<maxst;j++) dp[cur][j]=-INF;
            for(int j=0;j<three[m];j++){//注意枚举上界不再是(1<<M),不能使用位运算!
                if(dp[1-cur][j]==-INF) continue;
                int k=0;
                fenjie(j,k);
                dp[cur][k]=max(dp[cur][k],dp[1-cur][j]);//整行不放
                update(i,0,k,dp[1-cur][j]);
            }
        }
        for(int i=0;i<three[m];i++) ans=max(ans,dp[cur][i]);
        printf("%d\n",ans);
    }
    return 0;
}

zoj1346
题意:DAG图拓扑排序的个数统计
解法:dp[st]表示还有st状态的点未加入序列时,还能够有的排序方法数
删除已加入的点及相关边,然后枚举当前入度为0的点,加入st状态,记忆化搜索即可
复杂度分析: n22n

# include<iostream>
# include<cstdio>
# include<algorithm>
# include<queue>
# include<complex>
# include<stack>
# include<math.h>
# include<cstring>
#include<map>
#define ll long long
#define pi acos(-1)
#define MP make_pair
#define X first
#define Y second
using namespace std;

const int maxn = (1<<18);
int n,cnt;
map<string,int> ma;
char ch1[20],ch2[20];
int du[20];
int vis[20][20];
ll dp[maxn];

ll dfs(int st){
    if(dp[st]!=-1) return dp[st];
    int tmpdu[20];
    ll tmp=0;
    for(int j=0;j<cnt;j++) tmpdu[j]=du[j];
    for(int i=0;i<cnt;i++){//删点 减度
        if(st&(1<<i)){
            for(int j=0;j<cnt;j++) if(vis[i][j]){
                tmpdu[j]--;
            }
        }
    }
    for(int j=0;j<cnt;j++) if(!(st&(1<<j))&&!tmpdu[j]){
        tmp+=dfs(st|(1<<j));
    }
    return dp[st]=tmp;
}

int main(){
    //freopen("a.txt","r",stdin);
    while(scanf("%d",&n)!=EOF){
        ma.clear(); cnt=0;
        memset(du,0,sizeof(du));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++){
            scanf("%s%s",ch1,ch2);
            if(ma.find(string(ch1))==ma.end()) ma[string(ch1)]=cnt++;
            if(ma.find(string(ch2))==ma.end()) ma[string(ch2)]=cnt++;
            int u=ma[string(ch1)],v=ma[string(ch2)];
            vis[u][v]=1;
            du[v]++;
        }
        memset(dp,-1,sizeof(dp));
        dp[(1<<cnt)-1]=1;
        dfs(0);
        printf("%lld\n",dp[0]);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值