HDU2167 Pebbles 题解

题目大意:在 n * n 每格带权棋盘上取若干互不八相邻格,求最大权值和。

轮廓线动态规划。逐格转移,记状态 f[i, j, k] 为处理至第 (i, j) 格(设 0 ≤ i < n,1 ≤ j ≤ n),前 n 格的取舍状态为 k 的方案数,记 0 为未取,1 为已取,利用二进制状态压缩,则 k 二进制下从低位向高位第 l 位表示当前格前第 l 格是否取的状态,可知 k 最多只需记 n + 1 位即可。对于当前状态 k,转移分为以下四种情况:

  1. 当前列为最左列(即 j = 1),此时若格 (i - 1, j) 未取(即 k and 2n1 = 0),且格 (i - 1, j + 1) 未取(即 k and 2n2 = 0),则可以取格 (i, j),即状态 f[i - 1, n, k] 可以转移到状态 f[i, j, k * 2 and ( 2n+11 ) xor 1]。

  2. 当前列不为最左列且当前列不为最右列(即 1 < j < n),此时若格 (i - 1, j - 1) 未取(即 k and 2n = 0),且格 (i - 1, j) 未取(即 k and 2n1 = 0),且格 (i - 1, j + 1) 未取(即 k and 2n2 = 0),则可以取当前格 (i, j),即状态 f[i, j - 1, k] 可以转移到状态 f[i, j, k * 2 xor 1]。

  3. 当前列为最右列(即 j = n),此时若格 (i - 1, j - 1) 未取(即 k and 2n = 0),且格 (i - 1, j) 未取(即 k and 2n1 = 0),则可以取格 (i, j),即状态 f[i, j - 1, k] 可以转移到状态 f[i, j, k * 2 xor 1]。

  4. 可以不取当前格 (i, j),即状态 f[i, j - 1, k] (或状态 f[i - 1, n - 1, k]) 可以转移到状态 f[i, j, k * 2 and ( 2n+11 )]。

此题读入较为繁琐,除此之外与 HDU1565 类似。设初始状态 f[-1, n, 0] = 0,f[-1, n, 1 .. 2n+11 ] = -1(不合法状态),转移相当于维护最大值,则状态 f[n - 1, n, 0 .. 2n+11 ] 中的最大值即为答案。可以采用滚动数组优化,降低空间复杂度和编程复杂度。

代码如下:

#include<stdio.h>
#define max(a,b) ((a)>(b)?(a):(b))
int f[2][65540],val[20][20];
int main()
{
    int ans,cur,n;
    while(scanf("%d",&val[0][n=1])!=EOF)
    {
        while(getchar()!='\n')
            scanf("%d",&val[0][++n]);
        for(int i=1;i<n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&val[i][j]);
        for(int i=0;i<1<<n+1;i++)
            f[0][i]=f[1][i]=-1;
        cur=f[0][0]=ans=0;
        for(int i=0;i<n;i++)
            for(int j=1;j<=n;j++)
            {
                if(j==1)
                    for(int k=0;k<1<<n-2;k++)
                    {
                        if(f[cur][k]>-1&&f[cur^1][k<<1^1]<f[cur][k]+val[i][j])
                            f[cur^1][k<<1^1]=f[cur][k]+val[i][j];
                        if(f[cur][k^1<<n]>-1&&f[cur^1][k<<1^1]<f[cur][k^1<<n]+val[i][j])
                            f[cur^1][k<<1^1]=f[cur][k^1<<n]+val[i][j];
                    }
                else
                    for(int k=0;k<1<<n-1-(j<n);k+=2)
                        if(f[cur][k]>-1&&f[cur^1][k<<1^1]<f[cur][k]+val[i][j])
                            f[cur^1][k<<1^1]=f[cur][k]+val[i][j];
                for(int k=0;k<1<<n;k++)
                    f[cur^1][k<<1]=max(f[cur^1][k<<1],f[cur][k]),f[cur][k]=-1,
                    f[cur^1][k<<1]=max(f[cur^1][k<<1],f[cur][k^1<<n]),f[cur][k^1<<n]=-1;
                cur^=1;
            }
        for(int i=0;i<1<<n+1;i++)
            if(ans<f[cur][i])
                ans=f[cur][i];
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值