poj 1038 动态规划(dp)

本文介绍了一个涉及动态规划(DP)的算法问题,并通过状态压缩技巧将复杂度从4的10次方降低到了3的10次方,使得算法能够在实际中取得不错的效果。文章详细解释了状态压缩的方法及如何通过递归实现状态转移。
   这个题目其实要想到dp不难,关键一点在于压缩,很明显,我们只要保存临近两行的状态就可以保证无后效性。
但是这样状态还是太多了,4的10次方已经超过百万,注意到当i-1已经不能用时,i-2已经没有意义,所以可以把此时i-2的两种状态压缩成一种,这样就变成了3的10次方,不到六万,再乘以150,一红九千万的状态,加上滚动数组的运用,空间不会超,关键是时间复杂度,9000万的状态,加上状态转移用到dfs转移,这个复杂度不好估算。大概在100左右,似乎没有办法承受该复杂度,刚开始我也就纠结在这了
   可是这个复杂度其实是离平均值比较远的,因为实际上很多状态是达不到的,所以这个算法实际上是可以去的不错的效果的。
  
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int maxn=200;
int n,m,k;
int a[maxn][maxn];
int dp[2][60000];
int d[101],f[101];
int t1,t2;
int work(int s)
{
    for(int i=1;i<=m;i++)
    {

        if(d[i])
        f[i]=3;
        else
        f[i]=s%3;
        s/=3;
    }
    return(0);
}

int gethash()
{
    int sum=0,txt=1;
    for(int i=1;i<=m;i++)
    {
        if(f[i]>=3)
        sum+=txt*2;
        else if(f[i]==2)
        sum+=txt*1;
        txt*=3;
    }
    return(sum);
}

int dfs(int t,int ans)
{
//    for(int i=1;i<=m;i++)
//    printf("%d ",f[i]);
//    printf("%d %d\n",ans,t1);

    int sum=gethash();
    if(dp[t1][sum]<ans) dp[t1][sum]=ans;
    if(t>m) return(0);

    if(f[t]==0&&t+1<=m&&f[t+1]==0)
        {
            f[t]=3,f[t+1]=3;
            dfs(t+2,ans+1);
            f[t]=0,f[t+1]=0;
        }

    if(t+2<=m&&f[t]<=1&&f[t+1]<=1&&f[t+2]<=1)
        {
            f[t]+=3,f[t+1]+=3,f[t+2]+=3;
            dfs(t+3,ans+1);
            f[t]-=3,f[t+1]-=3,f[t+2]-=3;
        }
    dfs(t+1,ans);
}

int ini(int t)
{
    for(int i=0;i<60000;i++)
    dp[t][i]=-1;
    dp[t][59048]=0;
}

int main()
{
    int tcase;
    scanf("%d",&tcase);
    while(tcase--)
    {
        scanf("%d %d %d",&n,&m,&k);
        memset(a,0,sizeof(a));
        for(int i=1;i<=k;i++)
        {
            int x,y;
            scanf("%d %d",&x,&y);
            a[x][y]=1;
        }
        ini(0);
        ini(1);
        for(int k=1;k<=n;k++)
        {
             t1=k%2;
             t2=(k+1)%2;
            ini(t1);
            for(int i=1;i<=m;i++)
            d[i]=a[k][i];
            for(int i=0;i<60000;i++)
            if(dp[t2][i]!=-1)
            {
                work(i);
                dfs(1,dp[t2][i]);
            }
        }
        int answer=0;
        for(int i=0;i<60000;i++)
        if(dp[n%2][i]>answer)
        answer=dp[n%2][i];
        printf("%d\n",answer);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值