插头DP【入门】

 

强烈推荐网址:http://www.notonlysuccess.com/?p=931

 

hdu 1693 Eat the Trees

多回路的不用判联通状态,二进制即可,转移情况2*2种。 时间O(n*m*2^n)  空间O(n*2^n)

/**插头DP**/
#include <cstdio>
#include <cstring>

const int maxm=13 ;
const int maxn=1<<12;

typedef long long ll ;

ll dp[maxm][maxn];
int map[maxm][maxm];
/*
i行j列 轮廓线状态k。除了换行以外,每个状态只与同行的状态的前一列有关,可以减少一维空间
*/

int main ()
{
    int n,m;
    int cas;
    scanf("%d",&cas);
    for (int I=1 ; I<=cas ; ++I)
    {
        int cnt=0;
        scanf("%d%d",&n,&m);
        /*行列交换的优化、以及总可行格数为奇数的优化*/
        if(n>=m)
        for (int i=0 ; i<n ; ++i)
        {
            for (int j=1 ; j<=m ; ++j)
            {
                scanf("%d",&map[i][j]);
                if(map[i][j])cnt++;
            }
        }
        else
        {
            n^=m^=n^=m;
            for (int i=1 ; i<=m ; ++i)
            {
                for (int j=0 ; j<n ; ++j)
                {
                    scanf("%d",*(map+j)+i);
                    if(map[j][i])cnt++;
                }
            }
        }
        if(cnt&1)
        {
            printf("Case %d: There are 0 ways to eat the trees.\n",I);
            continue;
        }
        memset (dp , 0 , sizeof(dp));
        dp[0][0]=1;

        for (int i=0 ; i<n ; ++i)
        {
            if(i>0)
            for (int k=0 ; k<(1<<(m)) ; ++k)
            {
                dp[0][k<<1]=dp[m][k];//换行
                //printf("%d %d %o map==%d %lld\n",i,0,k,map[i][0],dp[i][0][k<<1]);
            }

            for (int j=1 ; j<=m ; ++j)
            {
                for (int k=0 ; k<(1<<(m+1)) ; ++k)
                {
                    int a=(1<<j);
                    int b=(1<<(j-1));
                    if(map[i][j])
                    {
                        dp[j][k]=dp[j-1][k^a^b];
                        if((!(k&a)) ^ (!(k&b)))dp[j][k]+=dp[j-1][k];
                    }
                    else
                    {
                        if((!(k&a)) && (!(k&b)))
                        {
                            dp[j][k]=dp[j-1][k];
                        }
                        else dp[j][k]=0;
                    }
                }// end for k
            }//end for j
        }// end for i
        //for (int i=0 ; i<(1<<(m+1)) ; ++i)
            //printf("%d\n" , dp[n-1][m-1][i]);
        printf("Case %d: There are %I64d ways to eat the trees.\n",I,dp[m][0]);
    }
    return 0;
}



 时间O(n*m*2^n)  空间O(2^n)

 

/**插头DP**/
#include <cstdio>
#include <cstring>

const int maxm=13 ;
const int maxn=1<<12;

typedef long long ll ;

ll dp[2][maxn];
int map[maxm][maxm];
/*
i行j列 轮廓线状态k。除了换行以外,每个状态只与同行的状态的前一列有关,可以减少一维空间
*/

int main ()
{
    int n,m;
    int cas;
    scanf("%d",&cas);
    for (int I=1 ; I<=cas ; ++I)
    {
        int cnt=0;
        scanf("%d%d",&n,&m);

        /*行列交换的优化、以及总可行格数为奇数的优化*/
        if(n>=m)
        for (int i=0 ; i<n ; ++i)
        {
            for (int j=1 ; j<=m ; ++j)
            {
                scanf("%d",&map[i][j]);
                if(map[i][j])cnt++;
            }
        }
        else
        {
            n^=m^=n^=m;
            for (int i=1 ; i<=m ; ++i)
            {
                for (int j=0 ; j<n ; ++j)
                {
                    scanf("%d",*(map+j)+i);
                    if(map[j][i])cnt++;
                }
            }
        }
        if(cnt&1)
        {
            printf("Case %d: There are 0 ways to eat the trees.\n",I);
            continue;
        }
        memset (dp , 0 , sizeof(dp));
        dp[0][0]=1;
        int t=1;
        for (int i=0 ; i<n ; ++i)
        {
            memset (dp[t] , 0 , sizeof(dp[t]));//必须清空
            if(i>0)
            {
                for (int k=0 ; k<(1<<(m)) ; ++k)
                {
                    dp[t][k<<1]=dp[t^1][k];//换行
                }
                t^=1;
            }
            for (int j=1 ; j<=m ; ++j,t^=1)
            {
                for (int k=0 ; k<(1<<(m+1)) ; ++k)
                {
                    int a=(1<<j);
                    int b=(1<<(j-1));
                    if(map[i][j])
                    {
                        dp[t][k]=dp[t^1][k^a^b];
                        if((!(k&a)) ^ (!(k&b)))dp[t][k]+=dp[t^1][k];
                    }
                    else
                    {
                        if((!(k&a)) && (!(k&b)))
                        {
                            dp[t][k]=dp[t^1][k];
                        }
                        else dp[t][k]=0;
                    }
                //printf("%d  %d  %o %I64d  %I64d\n" , i , j ,k,dp[t^1][k] , dp[t][k]);
                }// end for k
            }//end for j
        }// end for i
            //printf("%I64d\n" , dp[t][0]);
        printf("Case %d: There are %I64d ways to eat the trees.\n",I,dp[t^1][0]);
    }
    return 0;
}


 

HDU 4064 Carcassonne

 

RankAuthorExe. TimeExe. MemoryCode Len.LanguageDate
1Geners0MS256K2361BG++2011-10-15 16:55:39

 

比赛的时候一直纠结在4进制上 , 上下左右四个方向表示状态,没敢敲,怕超时, 后来看见达哥过了 , 问了下方法,原来是可以3进制的,只考虑F R C即可,当时比赛的时候脑子木了。

在看了fzq的代码之前 ,第一行状态和第一列状态都是特殊处理的,在fzq的代码里有个技巧,因为是4进制表示3进制,所有有一个冗余态,直接让这个状态表示万能态,即任何时候都能与其联通,于是代码简洁了不少 , 时间也快了 。

哈希表+4进制+位运算+轮廓线状态。

哈希表要尽量小一些, 时间都浪费在滚动数组的清空了。

TLE+MLE代码 , 没用哈希的纯映射方法, 空间各种浪费啊

#include <cstdio>
#include <cstring>

using namespace std;
const int maxn=1594324;//3^13=1594323;
const int mod = 1000000007;

int val[maxn];
int dir[4][4]={1, 2, 3, 0, 0, 1, 2, 3, 2, 3, 0, 1, 3, 0, 1, 2};// dir[][0-up 1-right 2-down 3-left];
int dp[2][45108864+123];//00 - F , 01 - C , 10 - R , 11 is redundancy
char  map_[13][13][5];
int t; //滚动用

void init()//put ternary in quaternary
{
    int cnt = 0;
    val[cnt++] = 0;
    val[cnt++] = 1;
    val[cnt++] = 2;

    int p = 1;
    while (cnt < maxn)
    {
        val[cnt++] = val[p]*4 + val[0];
        val[cnt++] = val[p]*4 + val[1];
        val[cnt++] = val[p++]*4 + val[2];
    }
}

inline int change(char ch)
{
    if(ch == 'F')return 0;
    if(ch == 'C')return 1;
    if(ch == 'R')return 2;
    return 100;
}

int r,c;
void dfs(int col, int sta, int uni)//
{
    if(col==0)
    {
        //printf("===sta===%d\n",sta);
        dp[0][sta]=(1+dp[0][sta])%mod;
        return ;
    }

    for (int i = 0; i < 4; ++i)
    {
        const char *str=map_[0][col-1];//
        //printf("%s",map_[0][col]);
        //printf("%d %d  dir==%d  %c\n",change(str[dir[i][3]]), uni, dir[i][3], str[dir[i][3]] );
        if( change(str[dir[i][1]])==uni )
        dfs(col-1 , (sta<<2)|(change(str[dir[i][2]])) , change(str[dir[i][3]]));
    }
}

int cuts;
void DP()
{
    t=0;
    for (int i=1 ; i<r ; ++i)
    {
        t^=1;
        memset (dp[t] , 0 , sizeof(dp[t]));
        for (int k=0 ; val[k]<=(1<<((c<<1)+2)) ; ++k)
        {
            dp[t][((val[k] & cuts)<<2) |3]=(dp[t^1][val[k]]+dp[t][((val[k] & cuts)<<2) |3])%mod;
            //printf("===%d  %d  %d  %d  %d\n", i , j , val[k] , (val[k]<<2 & cut[c]) , cut[c]);
        }
        for (int j=0 ; j<c ; ++j)
        {
            const char *str=map_[i][j];

            t^=1;
            memset (dp[t] , 0 , sizeof(dp[t]));
            if(j==0)
            {
                for (int k=0 ; val[k]<=(1<<(c<<1)+2) ; k+=3)
                if((val[k]&3)==0)
                {
                    int state=((val[k]>>2) & 3);
                    for (int d=0 ; d<4 ; ++d)
                    {
                        if(change(str[dir[d][0]])==state)
                        dp[t][ (val[k]&(~15)) | (change(str[dir[d][1]])<<2)
                                | change(str[dir[d][2]]) ]=(dp[t^1][val[k]|3]+dp[t][ (val[k]&(~15)) | (change(str[dir[d][1]])<<2)
                                | change(str[dir[d][2]]) ])%mod;
                    }
                }
                }
            else
            {
                for (int k=0 ; val[k]<=( 1<<((c<<1)+2) ) ; ++k)
                {
                    //if(!dp[j-1][val[k]])continue;
                    //printf("===%d  %d  %d  %s\n", i , j , val[k] ,str);
                    int sta=val[k];
                    int left=(val[k]>>(2*j) & 3);
                    int up=(val[k]>>(2*j+2) & 3);
                    //sta=sta^(left<<(2*j))^left<<
                    //printf("%s %d %d  %d %d\n",str,i,j ,c , k);
                    //printf("val==%d   left ==%d  up==%d  dp===%d  j==%d\n",val[k], left , up , dp[i][j-1][val[k]] , j);
                    if(dp[t^1][val[k]])
                    for (int d=0 ; d<4 ; ++d)
                    {
                        if( change(str[dir[d][0]])==up && change(str[dir[d][3]])==left )
                            dp[t][ val[k]&(~(15<<(j*2))) | (change(str[dir[d][2]])<<(2*j))
                                    | change(str[dir[d][1]])<<(2*j+2) ] =
                                    (dp[t^1][val[k]]+dp[t][ val[k]&(~(15<<(j*2))) | (change(str[dir[d][2]])<<(2*j))
                                    | change(str[dir[d][1]])<<(2*j+2) ] )%mod;

                                    //printf("option ==%d  %d\n" , val[k]&(~(15<<(j*2))) | (change(str[dir[d][2]])<<(2*j))
                                    //| change(str[dir[d][1]])<<(2*j+2) , dp[i][j-1][val[k]]);

                    }
                }
            }
        }
    }
}

int main ()
{
    init();
    int cas;
    scanf("%d", &cas);
    for (int I=1 ; I<=cas ; ++I)
    {
        scanf("%d%d", &r, &c);
        cuts=(1<<(c<<1))-1;
        for (int i = 0; i < r; ++i)
            for (int j = 0; j < c; ++j)
                scanf("%s", map_[i][j]);

        memset (dp, 0, sizeof(dp));

        dfs(c , 0 , 0);//DFS要倒着来,这样DP的时候能是正着的
        dfs(c , 0 , 1);
        dfs(c , 0 , 2);
        int ans=0;
        DP();

        for (int i=0 ; val[i]<=(1<<((c<<1)+2)) ; ++i)
        if(dp[t][val[i]])
        {
            ans=(dp[t][val[i]]+ans)%mod;
            //printf("val ==%d  dp==%d\n",val[i],dp[r-1][c-1][val[i]]);
        }
        printf("Case %d: %d\n",I,ans);
    }
    return 0;
}


 

AC代码(哈希):

  

#include <cstdio>
#include <cstring>

typedef int types;

using namespace std;

const int Limithash=100007;
const int mod=1000000007;
int dir[4][4]={1, 2, 3, 0, 0, 1, 2, 3, 2, 3, 0, 1, 3, 0, 1, 2};
int r, c, map[5];
int cuts;
int t;

struct Hashmap{
	int hash[Limithash], state[Limithash], sum[Limithash], total;
	void hash_in(types s, int data)
	{
		int pos=s%Limithash;//Limithash must be a prime 
		while (hash[pos])
		{
			if(state[hash[pos]]==s)//如果状态存在
			{
				sum[hash[pos]]=(sum[hash[pos]]+data)%mod;
				return;//找到并更新状态
			}
			pos++;
			if(pos==Limithash)pos=0;
		}
		total++;//从1开始
		hash[pos]=total;//此处添加新状态
		state[total]=s;
		sum[total]=data;
	}
	void clear ()
	{
		total=0;
		memset (hash , 0 , sizeof(hash));
	}
}dp[2];

inline void roll ()
{
	t^=1;
	dp[t].clear();
}

inline bool valid(int x, int y)
{
	return x==0 || x==y;
}

char str[10];

int main ()
{
	int cas;
	scanf("%d", &cas);
	for (int I=1 ; I<=cas ; ++I)
	{
		scanf("%d%d", &r, &c);
		cuts=(1<<(c<<1))-1;
		t=0;
		dp[0].clear();
		dp[0].hash_in(0, 1);
		for (int i=0 ; i<r ; ++i)
		{
			roll();
			for (int k=1 ; k<=dp[t^1].total ; ++k)//换行
				dp[t].hash_in( (dp[t^1].state[k] & cuts)<<2, dp[t^1].sum[k]);

			for (int j=0 ; j<c ; ++j)
			{
				roll();
				scanf("%s", str);
				for (int k=0 ; k<4 ; ++k)
				{
					if(str[k]=='C')map[k]=1;
					if(str[k]=='F')map[k]=2;
					if(str[k]=='R')map[k]=3;
				}
				for (int k=1 ; k<=dp[t^1].total ; ++k)
				{
					int sta=dp[t^1].state[k];
					int left=(sta>>(j<<1))&3;
					int up=(sta>>((j<<1)+2))&3;
					sta=sta^(left<<(j<<1))^(up<<((j<<1)+2));
					for (int d=0 ; d<4 ; ++d)
					{
						if(valid(left, map[dir[d][3]]) && valid(up, map[dir[d][0]]))
                            dp[t].hash_in( sta|(map[dir[d][1]]<<(j+1<<1))|(map[dir[d][2]]<<(j<<1)),
                                           dp[t^1].sum[k]);
					}
				}
			}
		}
		int ans=0;
		for (int i=1 ; i<=dp[t].total ; ++i)
		{
			ans=(ans+dp[t].sum[i])%mod;
		}
		printf("Case %d: %d\n", I, ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值