uva6625(状压dp)

3 篇文章 0 订阅

题意:有k行,接下来是每行有几个格子,再输入一个上限n,需要满足规则填数字。
规则1:每行右边的数大于等于左边
规则2:每列下面的数大于上面
规则3:填入的最大数字不超过n,不小于1


题解:注意到n只有7,又是递推(因为前面填入的数字会影响后面)可以联想到状压dp,因为每列的数字互不影响,我们把每列的状态压缩。预处理出每一列的状态,与上一列比较是否合法
dp[i][j]表示第i列第j种状态的数量
那么dp[i][j] = ∑(dp[i-1][k]) k是上一列的合法状态总数
如何判断合法? 就是当前列第i个数出现的位置一定要大于等于上一列第i个数出现的位置


#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <set>
#include <cmath>
#define LL long long
#define INF 0x3f3f3f3f
#define MOD 998244353
const int maxn = 1e5 + 5;
using namespace std;
int k,n;
int dp[10][1<<10];
int row[10];
int cow[10];
int state[10];//每一列有多少种状态
int s[10][1<<10];//每一列的所有状态
int a[10];
int b[10];
void init(){//预处理每一列有多少状态
    for(int i=1; i<=row[1]; i++){
        int sum = 0;
        for(int j=1; j<=k; j++){
            if(row[j] >= i) sum++;
        }
        cow[i] = sum;//每列有几个数
    }
    for(int i=1; i<=row[1]; i++){
        int cnt = 0;
        for(int j=0; j<=(1<<n)-1; j++){//所有满足状态的数
            int sum = 0;
            for(int k=0; k<=n; k++){
                if(((1<<k) & j) != 0)
                    sum++;
            }
            if(sum == cow[i])
                s[i][cnt++] = j;
        }
        state[i] = cnt;
    }
}
bool judge(int x, int y, int numb){//判断当前是否合法 x为当前,y为上一个状态 x第i个数出现的位置一定要>= y第i个数出现的位置
    int cnt = 1;
    for(int i=0; i<=n; i++){
        if(( (1 << i) & x) != 0){
            a[cnt++] = i;
        }
    }
    cnt = 1;
    for(int i=0; i<=n; i++){
        if(( (1 << i) & y) != 0){
            b[cnt++] = i;
        }
    }
    for(int i=1; i<=numb; i++){
        if(a[i] < b[i])
            return false;
    }
    return true;
}
int main(){
    while(cin >> k){
        memset(dp, 0, sizeof(dp));
        memset(state, 0, sizeof(state));
        for(int i=1; i<=k; i++){
            cin >> row[i];
        }
        cin >> n;
        init();
        for(int i=0; i<state[1]; i++){
            dp[1][i] = 1;
        }
        for(int i=2; i<=row[1]; i++){
            for(int k=0; k<state[i]; k++){
                for(int j=0; j<state[i-1]; j++){
                    if(judge(s[i][k],s[i-1][j],cow[i])){
                        dp[i][k] += dp[i-1][j];
                    }
                }
            }
        }
        int ans = 0;
        for(int i=0; i<state[row[1]]; i++){//最后一列的所有状态数
            ans += dp[row[1]][i];
        }
        cout << ans << endl;
    }
}
/*
1 1
1
1 1
2
2 2 1
4 
4 3 2 1 1
4
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值