HDU 1565 方格取数(1)(状压dp)

126 篇文章 2 订阅

感觉这道题目的数据比较水啊,程序的时间复杂度为1711^2*20竟然也可以过掉。。。。其他的就是状压了啊,注意需要滚动一下啊。。。。

方格取数(1)

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5701    Accepted Submission(s): 2159


Problem Description
给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
 

Input
包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)
 

Output
对于每个测试实例,输出可能取得的最大的和
 

Sample Input
  
  
3 75 15 21 75 15 28 34 70 5
 

Sample Output
  
  
188
 

#include <set>
#include <map>
#include <queue>
#include <math.h>
#include <vector>
#include <string>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>


using namespace std;

const int maxn = 22;
int dp[2][1<<21];
int mp[maxn][maxn];
int num[1<<22];

int judge(int x)
{
    for(int i = 0; i < 20; i++) if((x&(1<<i)) && (x&(1<<(i+1)))) return 1;
    return 0;
}

int main()
{
    int n;
    int pp = 0;
    for(int i = 0; i < (1<<20); i++) if(!judge(i)) num[pp++] = i;
    cout<<pp<<endl;
    while(~scanf("%d", &n))
    {
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++) scanf("%d",&mp[i][j]);
        memset(dp, 0, sizeof(dp));
        int flag;
        for(int p = 0; p < pp; p++)
        {
            int i = num[p];
            flag = 0;
            for(int j = 0; j < n; j++)
            {
                if((i&(1<<j)) && (i&(1<<(j+1))))
                {
                    flag = 1;
                    break;
                }
                if(i&(1<<j)) dp[0][i] += mp[0][j];
            }
            if(flag) dp[0][i] = 0;
        }

        for(int i = 1; i < n; i++)
        {
            for(int p = 0; p < pp && num[p] < (1<<n); p++)
            {
                int j = num[p];
                dp[1][j] = 0;
                int flag = 0;
                for(int k = 0; k < n-1; k++)
                {
                    if((j&(1<<k)) && (j&(1<<(k+1))))
                    {
                        flag = 1;
                        break;
                    }
                }
                if(flag) continue;
                for(int kk = 0; kk < n; kk++) if(j&(1<<kk)) dp[1][j] += mp[i][kk];
                int sum = 0;
                for(int tt = 0; tt < pp && num[tt] < (1<<n); tt++)
                {
                    int k = num[tt];
                    if(!dp[0][k] && k > 0) continue;
                    if(j&k) continue;
                    sum = max(sum, dp[0][k]);
                }
                dp[1][j] += sum;
            }
            for(int j = 0; j < pp && num[j] < (1<<n); j++) dp[0][num[j]] = dp[1][num[j]];
        }
        int Max = 0;
        for(int i = 0; i < pp && num[i] < (1<<n); i++) Max = max(dp[0][num[i]], Max);
        printf("%d\n",Max);
    }
    return 0;
}

/*
20
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值