POJ - 1185 and POJ - 3254(经典状压dp)

题目:click POJ-1185
题意:在一个给定的字符串地图里放炮兵,炮兵的攻击范围,上下两格,左右两格,H的地方不能放炮兵,炮兵相互之间攻击不到,问地图最多放多少个炮兵。

写了挺久有些细节没注意。。。。首先分析题目,m范围可以进行状态压缩,对单独一个网格进行分析,当是P的时候可以进行放或者不放,放的话我需要考虑上下左右的两个格子都没有被放过炮兵,一开始想以当前格点为右下的顶点最多多少个,状态转移有问题,状态枚举也不能很好预处理。
直接考虑上面两行的状态会简单很多,左右两边如果直接枚举(1<<m)最大的时候写的话一定会T。打表发现一行放炮兵满足两个相互攻击不到最多60个,这么就好做多了。预处理一行可行的状态,记得再对H进行检查,0表示没放 1表示放置了, H的地方不能放置。
dp[i][j][k]:第i行 上一行的状态j 本行状态k。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
char a[110][12];
int s[63];//两个炮兵相互不攻击的状态
int book[63];//状态中放了多少个炮兵
int n,m;
int dp[105][63][63];//第i行 上一行的状态是j  这一行的状态k 最大炮兵数
inline bool check(int y)
{
    int i=0;
    while(i<m)
    {
        if((1<<i)&y)
        {
            i++;
            if(i>=m)
                return true;
            if(((1<<i)&y))
                return false;
            i++;
            if(i>=m)
                return true;
            if(((1<<i)&y))
                return false;
        }
        else
            i++;
    }
    return true;
}
inline int solve(int x)
{
    int cnt=0;
    for(int i=0;i<m;i++)
    {
        if(x&(1<<i))
            cnt++;
    }
    return cnt;
}
inline bool check1(int x,int y)
{
    int cnt=0;
    for(int i=m-1;i>=0;i--,cnt++)
    {
        if(a[x][i]=='H')
        {
            if(!((1<<cnt)&y))
                continue;
            else
                return false;
        }
    }
    return true;
}
int main()
{
    memset(book,0,sizeof(book));
    memset(dp,0,sizeof(dp));
    int i,j,k;
    int len=0;
    scanf("%d %d",&n,&m);
    for(i=0;i<n;i++)
        scanf("%s",a[i]);
    for(i=0;i<(1<<m);i++)
    {
        if(check(i))
        {
            book[len]=solve(i);
            s[len++]=i;
        }
    }
    for(k=0;k<len;k++)
    {
        if(check1(0,s[k]))
        dp[0][0][k]=book[k];
    }
    for(i=0;i<len;i++)
    {
        for(j=0;j<len;j++)
        {
            if((s[i]&s[j])==0&&check1(0,s[i])&&check1(1,s[j]))
            dp[1][i][j]=max(book[j]+dp[0][0][i],dp[1][i][j]);
        }
    }
    for(i=2;i<n;i++)//第i行
    {
        for(j=0;j<len;j++)//第i-1行的状态
        {
            if((check1(i-1,s[j]))==false)
            continue;
            for(k=0;k<len;k++)//第i行的状态
            {
                if((check1(i,s[k]))==false)
                continue;
                for(int hh=0;hh<len;hh++)//第i-2行的状态
                {
                    if((check1(i-2,s[hh]))==false)
                        continue;
                    if((s[j]&s[k])||(s[j]&s[hh])||(s[k]&s[hh]))
                    {
                        continue;
                    }
                    dp[i][j][k]=max(dp[i][j][k],book[k]+dp[i-1][hh][j]);
                }
            }
        }
    }
    int ans=0;
    for(i=0;i<len;i++)
    {
        for(j=0;j<len;j++)
        {
            if(s[i]&s[j])
                continue;
            ans=max(ans,dp[n-1][i][j]);
        }
    }
    printf("%d",ans);
    return 0;
}

题目:click poj-3254
题意:给定一个图,1表示可以放牛,0不行,相邻的格子不能同时放牛,问一共多少种方法。

具体分析跟上面的题目一样,我们只需要知道上一行的状态,预处理出行可行的状态并且检查0是否放牛。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
const int mod=100000000;
int a[15][15];
int dp[15][400];//第i行 j状态的MAX方法数
int s[400];//一行可存在的状态
int n,m;
inline bool check(int y)
{
    int i=0;
    while(i<m)
    {
        if((1<<i)&y)
        {
            i++;
            if(i>=m)
                return true;
            if(((1<<i)&y))
                return false;
        }
        else
            i++;
    }
    return true;
}
inline bool check1(int x,int y)
{
    int cnt=0;
    for(int i=m-1;i>=0;i--,cnt++)
    {
        if(a[x][i]==0)
        {
            if((1<<cnt)&y)
            {
                return false;
            }
            else
            continue;
        }
    }
    return true;
}
int main()
{
    memset(dp,0,sizeof(dp));
    int i,j,k;
    int len=0;
    scanf("%d %d",&n,&m);
    for(i=0;i<n;i++)
    {
        for(j=0;j<m;j++)
        {
           cin>>a[i][j];
        }
    }
    for(i=0;i<(1<<m);i++)
    {
        if(check(i))
        {
            s[len++]=i;
        }
    }
    for(i=0;i<len;i++)
    {
        if(check1(0,s[i]))
        dp[0][i]=1;
    }
    for(i=1;i<n;i++)
    {
        for(j=0;j<len;j++)//本行
        {
            if((check1(i,s[j]))==false)
                continue;
            for(k=0;k<len;k++)//上一行行状态
            {
                if((check1(i-1,s[k]))==false)
                    continue;
                if(s[j]&s[k])
                    continue;
                dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
            }
        }
    }
    int ans=0;
    for(i=0;i<len;i++)
    {
        ans+=dp[n-1][i];
        ans%=mod;
    }
    printf("%d",ans);
    return 0;
}

发布了75 篇原创文章 · 获赞 19 · 访问量 7794
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览