Ural1519 Formula 1 插头dp入门

看了几天才懂,自己也说不太清楚,推荐看CDQ的论文基于连通性状态压缩的动态规划问题
这个看完之后这里有一篇比较详细的解题报告http://blog.sina.com.cn/s/blog_51cea4040100gmky.html
然后说说自己的理解吧,因为先学的轮廓线,做了几道题目,所以在状态转移上面还是比较清楚是怎么回事,这道题一直没有弄懂的是怎么用hash存状态,因为以前从来没有用过hash写题,所以卡了很久,看别人的代码后有了一点理解,如果采用最小表示法来表示,状态有每一位状态有7种,那么就需要一个三位二进制来表示一个格子,这样得到的状态用整数表示就有36位,枚举状态一定超时,其实不难发现,有很多状态都是无效的(因为不能交叉)所以题解用hash来存有效状态,从初始状态扩展出来,这样有效的避免了很多无用状态,这个状态扩展一开始也没有理解,其实这个当中的思路很像bfs 从已得到的状态来扩展其他状态。
用最小表示法的代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cstring>

using namespace std;
#define LL long long
int mp[15][15];
const int N = 30005;
struct _Hash{
    int hs[N];
    LL dat[N];
    int _size;
    void _init(){
        _size = 0;
        memset(hs,-1,sizeof(hs));
    }
    int Push(LL s,bool &ishave)
    {
        int ss = s%N;
        while(hs[ss] != -1 && dat[hs[ss]]!=s)
            ss = (ss+1)%N;
        if(hs[ss]!=-1)
        {
            ishave = true;
            return hs[ss];
        }
        ishave = false;
        hs[ss] = _size;
        dat[_size++] = s;
        return _size-1;
    }
};
struct node{
    _Hash hs;
    LL val[N];
    void _init()
    {
        hs._init();
        memset(val,0,sizeof(val));
    }
    void push(LL s,LL num)
    {
        bool ishave;
        int id = hs.Push(s,ishave);
        if(ishave)
        {
            val[id] += num;
        }
        else val[id] = num;
    }
}dp[2];
int n,m;
int code[15];
void deCode(LL s)
{
    int tail = m+1;
    while(tail--)
    {
        code[tail] = s&((1<<3)-1);
        s>>=3;
    }
}
LL enCode()
{
    LL ans = 0;
    int ton[10];
    memset(ton,-1,sizeof(ton)); // 用于扫描 把状态表示为最小表示
    ton[0] = 0;
    int cnt = 1;
    for(int i = 0;i<=m;i++)
    {
        if(ton[code[i]]==-1)
        {
            ton[code[i]] = cnt++;
        }
        code[i] = ton[code[i]];
        ans <<= 3;
        ans |= code[i];
    }
    return ans;
}
int main()
{

    while(scanf("%d%d",&n,&m)!=EOF)
    {
        char str[15];
        memset(mp,0,sizeof(mp));
        int ei,ej;
        ei = ej = 0;
        for(int i = 0;i<n;i++)
        {
            scanf("%s",str);
            for(int j = 0;j<m;j++)
            {
                if(str[j]=='*')
                    mp[i][j] = 0;
                else mp[i][j] = 1,ei = i,ej = j;
            }
        }
        int cur = 0;
        dp[cur]._init();
        dp[1-cur]._init();
        dp[cur].push(0,1);
        LL ans = 0;
        for(int i = 0;i<n;i++)
        {
            for(int j = 0;j<m;j++)
            {
                for(int k = 0;k<dp[cur].hs._size;k++)
                {
                    LL sta = dp[cur].hs.dat[k];
                    LL num = dp[cur].val[k];
                    if(mp[i][j]==0)
                    {
                        dp[1-cur].push(sta,num);
                        continue;
                    }
                    deCode(sta);
                    //for(int ii = 0;ii<m;ii++)cout << code[ii] <<" ";
                    //cout << endl;
                    if(code[j]!=0 && code[j+1]!=0) // 连通
                    {
                        if(code[j]==code[j+1] && i == ei && j == ej)
                        {
                            ans += num;
                        }
                        else if(code[j]!=code[j+1])
                        {
                            int temp = code[j];
                            int jp = code[j+1];
                            code[j] = 0;
                            code[j+1] =0;
                            for(int ii = 0;ii<=m;ii++)
                            {
                                if(code[ii]==temp)
                                {
                                    code[ii] = jp;
                                }
                            }
                            dp[1-cur].push(enCode(),num);
                        }
                    }
                    else if(code[j]^code[j+1])
                    {
                        int ma = max(code[j],code[j+1]);
                        if(mp[i+1][j])
                        {
                            code[j] = ma;
                            code[j+1] = 0;
                            dp[1-cur].push(enCode(),num);
                        }
                        if(mp[i][j+1])
                        {
                            code[j] = 0;
                            code[j+1] = ma;
                            dp[1-cur].push(enCode(),num);
                        }
                    }
                    else
                    {
                        if(mp[i+1][j] && mp[i][j+1])
                        {
                            code[j] = 9;
                            code[j+1] = 9;
                            dp[1-cur].push(enCode(),num);
                        }
                    }

                }
                dp[cur]._init();
                cur = 1^cur;

            }
            for(int i = 0;i<dp[cur].hs._size;i++)
            {
                dp[1-cur].push(dp[cur].hs.dat[i]>>3,dp[cur].val[i]);
            }
            dp[cur]._init();
            cur = 1^cur;
           /* if(i==1 && j==0)
            {
                cout << dp[cur].hs._size<< " ";
                cout << dp[cur].val[dp[cur].]
            }*/
        }
        printf("%lld\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值