Gym 100917M Matrix, The(状压dp)

  题目大意:给n,a,b,q。一个n行n列矩阵,当且仅当每行有[a,b]个1且每列有一段连续的1才是合法的,q次询问,问第t字典序大的矩阵的样子,a,b,n<=10,t<=1e18,q<=1000
  思路:牛逼题,像数位dp的思路。画张图理解一下。

上图,s表示每行的状态,当si->sj的转移是合法的时候我们才向下面连边(上图默认都是合法的了),可以发现很多状态都是重复的,和数位dp一样的思路——记忆化搜索。
  我们用dp[r][state][mask]表示第r行状态为state,mask表示从1~r行每列是否出现过1的方案数。求来后,对于询问第t个,我们只要每层按照字典序搜过去就解决了。
  接下来看转移方程,发现“每列一段连续1这个条件不好处理”,所以我们引入mask参数,表示1~r行的每列是否出现过1,可以发现不合法的情况是,在这里插入图片描述

如图,下一行出现1,本行有0,之前出现过了1,这样就好判断相邻两行间转移了。
dp[r][state][mask] -> dp[r+1][next_state][next_state | mask]。
牛逼题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(false),cin.tie(0) 
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pii pair<int ,int >
#define pb(v) push_back(v)
#define all(v) v.begin(),v.end()
#define int long long
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define endl "\n"
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
#define AC return 0
const int N=1e5+10;
const int mod=1e9+7;
const double eps=1e-8;
int n,a,b,q;
int ans[11];
int f[11][1<<10][1<<10];
std::vector<int> V;//合法状态
//mask 表示到r行,每列是否出现过1
int dfs1(int r,int sta ,int mask){
    int &temp = f[r][sta][mask];
    if( temp>-1 ) return temp;
    if( r==n ){
         return temp = mask==((1<<n)-1);//每列至少一个1
    }
    temp = 0;
    for(int nxt:V){
        //不合法的情况就是有
        //1
        //0,,中间可能有若干0
        //1这种情况
        if( mask&(~sta)&nxt) continue;
        temp += dfs1(r+1,nxt,nxt|mask);
    }
    return temp;
}
bool dfs2(int k,int r,int sta,int mask){
    if( r==n ) return true; 
    for(int nxt:V){
        if( mask&(~sta)&nxt) continue;
        if( k <= f[r+1][nxt][nxt|mask] ) {
            ans[r+1] = nxt;
            return dfs2(k,r+1,nxt,nxt|mask);
        }
        else k -= f[r+1][nxt][nxt|mask];
    }
    return false;
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif  
    IOS;
    mst(f,-1);
    cin>>n>>a>>b>>q;
    for(int i=1 ;i<(1<<n) ;i++){
        int cnt=0;
        for(int j=0 ;j<n ;j++) cnt += i>>j&1;
        if( a<=cnt && cnt<=b ) V.push_back(i);
    }
    dfs1(0,0,0);
    while( q-- ){
        int x;cin>>x;
        if( dfs2(x,0,0,0) ){
            _for(i,1,n) {
                for(int j=n-1 ;j>=0 ;j--) cout<<(ans[i]>>j&1);
                cout<<endl;
            }
            cout<<endl;
        }
        else {
            cout<<"No such matrix."<<endl;
        }
    }
    AC;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值