【解题报告】2016.8.5·Day2·状压DP

今天的练习是二选一,最短路和DP,于是我们果断选DP,毕竟DP的内容很多我们都不大熟练,而最短路的内容至少我们还有过练习。
感觉对于状压并不陌生但又有点boom~,主要是刚开始时对于如何转移还是有不懂,不过在练了两题加上ppt内容理解的情况下,对于这玩意的简单运用还是可以的,T3很容易就AC了,只是T4感觉好高深!!
那么就这样开始了。

T1 Corn Fields

Time Limit:2000MS Memory Limit:65536KB

Description

Farmer John has purchased a lush new rectangular pasture composed of M by N(1M12;1N12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can’t be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.

Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.

Input
Line 1: Two space-separated integers: M and N
Lines 2.. M+1 : Line i+1 describes row i of the pasture with N space-separated integers indicating whether a square is fertile (1 for fertile, 0 for infertile)
Output
Line 1: One integer: the number of ways that FJ can choose the squares modulo 100,000,000.
Sample Input
2 3
1 1 1
0 1 0
Sample Output
9
Hint
Number the squares as follows:
1 2 3
4

There are four ways to plant only on one squares (1, 2, 3, or 4), three ways to plant on two squares (13, 14, or 34), 1 way to plant on three squares (134), and one way to plant on no squares. 4+3+1+1=9.

【题目分析】
毕竟今天做的题三题英文(不过有翻译),大意是……智障的 FJ要在一块地里种玉米,而种的玉米不能相邻,否则会影响发育。不过这块地只有一些地方适合种玉米,那么就用 1 表示。现在要求一共有多少种不同的种法(可以不种哦)。

【解题思路】
这道题 ...... 既然告诉我们是状压那就压呗,刚开始我是表示懵比的,但看了ppt感觉不难。其实就是把每一行的状态用一个01序列,即二进制来表示每个位置放或没放。
我们想,因为每一行的状态只是和上一行有关,那么我们就可以循环两行的状态数,然后for循环 n 行,这样就可得到答案,但时间复杂度是O(2m2mn),显然这是会超。但实际上,因为相邻两个不能同时种,显然种数会少很多,那么我们就可以先把所有可以的情况打表出来,然后再 DP ,就可以了。

【AC代码】

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int mod=100000000;

int matrix[13],canuse[5000];
int dp[13][5000];
int n,m,tk,ans,MAX;

void readin()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
//matrix[i]<<=1;
            scanf("%d",&tk);
            if(tk==0)
                matrix[i]+=(1<<(j-1));
        }
    }

    for(int i=0;i<(1<<m);++i)
        if(!(i&(i<<1)))
        {
//printf("%d\n",i);
            canuse[MAX++]=i;
        }
}

void solve()
{
    for(int i=0;i<MAX;++i)
        if( !(matrix[1] & canuse[i]) )
            dp[1][i]=1;

    for(int i=2;i<=n;++i)
    {
        for(int j=0;j<MAX;++j)
        {
            if(matrix[i] & canuse[j])
                continue;

            for(int k=0;k<MAX;++k)
            {
                if(matrix[i-1] & canuse[k])
                    continue;
                if(!(canuse[j] & canuse[k]))
                {
                    dp[i][j]+=dp[i-1][k];
                    dp[i][k]%=mod;
                }
            }
        }
    }

    for(int i=0;i<MAX;++i)
    {
        ans+=dp[n][i];
        ans%=mod;
    }
    printf("%d\n",ans);
}

void debug()
{
    printf("canuse: ");
    for(int i=0;i<MAX;++i)
        printf("%d ",canuse[i]);
    printf("\n");

    for(int i=1;i<=n;++i)
        printf("%d\n",matrix[i]);
    for(int i=1;i<=n;++i)
    {
        for(int j=0;j<10;++j)
            printf("%d ",dp[i][j]);
        printf("\n");
    }
}

int main()
{
    readin();
    solve();
//  debug();
    return 0;
}

一定要mod!!!

==========================华丽丽的分割线==========================

T2 Mondriaan’s Dream

Time Limit:3000MS Memory Limit:65536KB

Description
Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his ‘toilet series’ (where he had to use his toilet paper to draw on, for all of his paper was filled with squares and rectangles), he dreamt of filling a large rectangle with small rectangles of width 2 and height 1 in varying ways.

Expert as he was in this material, he saw at a glance that he’ll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won’t turn into a nightmare!
Input
The input contains several test cases. Each test case is made up of two integer numbers: the height h and the width w of the large rectangle. Input is terminated by h=w=0. Otherwise, 1<=h,w<=11.
Output
For each test case, output the number of different ways the given rectangle can be filled with small rectangles of size 2 times 1. Assume the given large rectangle is oriented, i.e. count symmetrical tilings multiple times.
Sample Input
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
Sample Output
1
0
1
2
3
5
144
51205

【题目分析】
很经典的题目,铺骨牌,用 1×2 的骨牌把 n×m 的棋盘填完,求一共有多少种方法。

【解题思路】
首先有三个性质:

性质1:如果w和h都是奇数,则无解,否则有解。
证明:w,h都是奇数,则w*h也是奇数,由于1×2的砖有2块,因此无论铺多少块都是偶数,因此不能覆盖所有的地板。如果地板的面积S是偶数,肯定能被2整除,因此可以用S/2块砖铺满整个地板。

性质2:对于每铺一次地板,只会影响所铺的上下两行。
证明:因为是1×2的砖铺,性质显然。

性质3:如果按行铺地板,每一行的铺法都类似。
证明:显然!

那么我们很容易得出状态转移方程:
设dp(i,j)表示铺到第i行,状态为j的方案数,则
dp(i,j)=f(i1,j2)
其中j2表示能转移到j的状态,显然复杂度为 O(h2w)

接下来讨论如何放:
显然,对于该行某个位置j,
如果前一行该位置为0,那么该位置可以竖放 即 0-> 1;
如果前一行连续两个位置为0,那么这两个连续位置可以横放 即00-> 00;
如果前一行该位置为1,显然该位置不能再放,于是应该把该位置设置为0 ,即1-> 0。

好的就是这样,转移就是这样。

【AC代码】

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAX=(1<<11)+2;

int n,m,M;
long long tmpmark,dp[13][MAX];

void dfs(int line,int now,int p)//line,status,point 
{
    if(p>=m)
    {
        dp[line][now]+=tmpmark;
        return;
    }
    dfs(line,now,p+1);
    if(p<=m-2 && !( now & ( 1<<(m-p-1)) ) && !(now & ( 1<<(m-p-2))) )//nothing in p and nothing in p+1
        dfs(line,now|(1<<(m-p-1))|(1<<(m-p-2)),p+2);
}

void solve()
{
    if(n&1 && m&1)
    {
        printf("0\n");
        return;
    }

    M=1<<m;
    memset(dp,0,sizeof(dp));

    tmpmark=1;
    dfs(1,0,0);
    for(int i=2;i<=n;++i)
    {
        for(int j=0;j<M;++j)
        {
            if(!dp[i-1][j])
                continue;
            tmpmark=dp[i-1][j];
            dfs(i,~j&(M-1),0);
        }
    }

    printf("%lld\n",dp[n][M-1]);
}

int main()
{
    for(;;)
    {
        scanf("%d%d",&n,&m);
        if(n==0)
            return 0;

        solve();
    }
}

我这里用dfs来枚举每一种可能的横铺情况,当然大多数情况下也适用。
注意要用long long!!

==========================华丽丽的分割线==========================

T3炮兵阵地
Time Limit:2000MS Memory Limit:65536KB
Description
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用”H” 表示),也可能是平原(用”P”表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

(图略,你懂的)

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
Input
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符(‘P’或者’H’),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
Output
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
Sample Input
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
Sample Output
6

【题目分析】
题目很清晰,但唯一不同这次要影响两行,所以我们可以想,dp是不是要记录两行……

【解题思路】
接上面,然后我们可以记录前两行炮的位置~,也就是dp[i][j][j2]表示在第i行,状态为j,它上一行状态为j2的设置的炮的最大值。
有过dfs+状压的解题经历,果断就会了……
这题时间复杂度本来是 O(2m2m2mn) ,果断爆炸,然而实际上还是有很多多余情况……照旧,把有用的提出来就好,然后就很简单了,我都能一次打过AC……

【AC代码】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAX=(1<<10)+2;

int n,m,sum,ans;
int matrix[102],canuse[1002],tot[1002];
int dp[102][MAX][MAX];
bool bo[1002];

int get_tot(int x)
{
    int t=0;
    while(x)
    {
        if(x&1) ++t;
        x>>=1;
    }
    return t;
}

void dfs_get_canuse(int now,int p)
{
    if(!bo[now])
    {
        tot[sum]=get_tot(now);
        canuse[sum++]=now;
        bo[now]=true;
    }
    if(p>m)
        return;
    if( !(now & (1<<(m-p)) ))
        dfs_get_canuse(now+(1<<(m-p)) , p+3);
    dfs_get_canuse(now,p+1);
}

void readin()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        getchar();
        for(int j=1;j<=m;++j)
        {
            char ch=getchar();
            matrix[i]<<=1;
            if(ch=='H')
                ++matrix[i];
        }
    }
}

void solve()
{
    for(int i=0;i<sum;++i)
        if(!(matrix[1] & canuse[i]))
            dp[1][i][0]=tot[i];

    for(int i=0;i<sum;++i)
    {
        if(!(matrix[1] & canuse[i]))
        for(int j=0;j<sum;++j)
            if(!(matrix[2] & canuse[j]) && !(canuse[i] & canuse[j]))
                dp[2][j][i]=max(dp[2][j][i] , dp[1][i][0]+tot[j]);
    }

    for(int i=3;i<=n;++i)
    {
        for(int j=0;j<sum;++j)//now line
        {
            if(matrix[i] & canuse[j])
                continue;

            for(int j2=0;j2<sum;++j2)//last one
            {
                if(matrix[i-1] & canuse[j2] || canuse[j] & canuse[j2])
                    continue;

                for(int j3=0;j3<sum;++j3)
                {
                    if(matrix[i-2] & canuse[j3] || canuse[j] & canuse[j3] || canuse[j2] & canuse[j3])
                        continue;

                    dp[i][j][j2]=max(dp[i][j][j2] , dp[i-1][j2][j3]+tot[j]);
                }
            }
        }
    }

    for(int i=0;i<sum;++i)
        for(int j=0;j<sum;++j)
            ans=max(ans , dp[n][i][j]);
    printf("%d\n",ans);
}

void debug()
{
    for(int i=0;i<sum;++i)
        printf("%d ",canuse[i]);
}

int main()
{
    readin();
    dfs_get_canuse(0,1);
    solve();
//debug();
}

==========================华丽丽的分割线==========================

T4D - DNA Laboratory
Time Limit:5000MS Memory Limit:30000KB

Description
Background
Having started to build his own DNA lab just recently, the evil doctor Frankenstein is not quite up to date yet. He wants to extract his DNA, enhance it somewhat and clone himself. He has already figured out how to extract DNA from some of his blood cells, but unfortunately reading off the DNA sequence means breaking the DNA into a number of short pieces and analyzing those first. Frankenstein has not quite understood how to put the pieces together to recover the original sequence.
His pragmatic approach to the problem is to sneak into university and to kidnap a number of smart looking students. Not surprisingly, you are one of them, so you would better come up with a solution pretty fast.
Problem
You are given a list of strings over the alphabet A (for adenine), C (cytosine), G (guanine), and T (thymine),and your task is to find the shortest string (which is typically not listed) that contains all given strings as substrings.
If there are several such strings of shortest length, find the smallest in alphabetical/lexicographical order.
Input
The first line contains the number of scenarios.
For each scenario, the first line contains the number n of strings with 1 <= n <= 15. Then these strings with 1 <= length <= 100 follow, one on each line, and they consist of the letters “A”, “C”, “G”, and “T” only.
Output
The output for every scenario begins with a line containing “Scenario #i:”, where i is the number of the scenario starting at 1. Then print a single line containing the shortest (and smallest) string as described above. Terminate the output for the scenario with a blank line.
Sample Input
1
2
TGCACA
CAT
Sample Output
Scenario #1:
TGCACAT

【题目分析】
有多组数据,你懂的。
把每组数据下的字符串连起来,可以在中间添加字符,最后形成的字符串要包括每一个字串。
输出最短、字典序最小的字符串

【解题思路】
不会……
现在的思路是:
有15个单词,果断想到状态是每个单词是否用过,那么第二个状态我觉得可能是最后使用的单词……然后我就不会了。
不过想了想,好像可以求出如果一词要接在另一词后面所需的字母数,这样是一个图咯。
对了如果有包含关系的字串(如AAB和AB),把被包含的删去就好。

【AC代码】
未完成

==========================华丽丽的分割线==========================

那么今天的状压DP就告一段落,说难也不是很难,问题就在如何转移,如何表示上了。
好吧明天的内容会更加智障。
wqnmlgb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值