南京2013区域赛C题,HDU4804

**队友用dfs过了,回来学了发轮廓线dp,现学现卖。
看过轮廓线后,觉得这玩意儿好神奇,写起来比dfs顺手多了,虽然两者的思想其实都是差不多的,都是从一个合法状态通过边转移到另一个状态,dfs难写,长,轮廓线好写,容易写错。思路必须要清晰。
不说废话了,轮廓线的经典之处在于每次枚举旧的状态。通过旧的状态走到新状态的时候,旧的状态先左移一位,再建立新状态,通过这样移位的操作,就不用傻傻的在最内层循环通过dfs来搜状态进行转移了,直接位运算搞定。每次更新的都是第m-1位(假设行为n,列为m的话),
并且在m-1位上方的点就是(sta&(1<<m-1)) sta表示旧状态,在m-1左边一个位置的点就是第0位,具体看代码转移**
//
//  Created by Running Photon on
//  Copyright (c) 2015 Running Photon. All rights reserved.
//
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#include <set>
#include <vector>
#include <stack>
#define ALL(x) x.begin(), x.end()
#define INS(x) inserter(x, x,begin())
#define ll long long
#define CLR(x) memset(x, 0, sizeof x)
using namespace std;
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 1e2 + 10;
const int maxv = 1e1 + 10;
const double eps = 1e-9;

int n, m, c, d;
int mp[maxn][maxv];
int state[maxn];
int cur;
ll dp[2][(1 << 10) + 10][25];
void update(int a, int b, int c, int d) {
    if(b & (1 << m)) dp[cur][b^(1<<m)][c] = (dp[cur][b^(1<<m)][c] + dp[1-cur][a][d]) % MOD;
}
int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
//  ios_base::sync_with_stdio(0);
    while(scanf("%d%d%d%d", &n, &m, &c, &d) != EOF) {
        CLR(state); CLR(dp);
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                scanf("%1d", mp[i] + j);
                mp[i][j] ^= 1;
                state[i] = state[i] * 2 + mp[i][j];
            }
        }
//        for(int i = 0; i < n; i++) {
//            for(int j = 0; j < m; j++) {
//                printf("%d ", mp[i][j]);
//            }
//            puts("");
//        }
        dp[0][(1 << m) - 1][0] = 1;
        cur = 0;
//        int flag = 0;
//        for(int i = 0; i < n; i++){
//            if(state[i] != (1 << m) - 1) flag = 1;
//        }
//        if(!flag) {puts("0"); continue;}
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                cur ^= 1;
                CLR(dp[cur]);
                for(int sta = 0; sta < (1 << m); sta++) {
                    for(int k = 0; k <= d; k++) {
                        if(mp[i][j]) {
                            update(sta, (sta << 1) ^ 1, k, k);
                            continue;
                        }
                        update(sta, sta << 1, k, k);
                        if(k != d)
                            update(sta, (sta << 1) ^ 1, k+1, k);
                        if(i && !(sta & (1 << m - 1)))
                            update(sta, (sta << 1) ^ (1 << m) ^ 1, k, k);
                        if(j && !mp[i][j-1] && !(sta & 1))
                            update(sta, (sta << 1) ^ 3, k, k);
                    }
                }
            }
        }
        ll ans = 0;
        for(int i = c; i <= d; i++) {
            ans = (ans + dp[cur][(1 << m) - 1][i]) % MOD;
        }
        printf("%lld\n", ans);
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值