压缩DP

1.铺砖问题

题目:给定n*m的格子,每个格子被染成了黑色或者白色。现在要用1 * 2 的砖块覆盖这些格子,要求块与块之间互相不重叠,且覆盖了所有白色的格子,但不覆盖任意一个黑色格子。求一个有多少种覆盖方法,输出方案数对M取余后的结果。

#include<bits/stdc++.h>
using namespace std;
bool M[17][17];
int dp[2][1<<15]; 
int main(){
    int n,m,mod;
    while(cin>>n>>m &&n&&m){
        cin>>mod;
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){ //从0开始后面会好操作一点 
                char input; cin>>input;
                if(input=='.') M[i][j]=0; //0:白 1:黑,将0全变为1 
                else M[i][j]=1;
            }
        }
        int *now=dp[0],*nex=dp[1];
        now[0]=1;
        for(int i=n-1;i>=0;i--){ //从n-1 开始会方便二进制表示状态 
            for(int j=m-1;j>=0;j--){
                for(int used=0;used< (1<<m) ;used++){ //遍历状态,这里反过来表示  
                    if(used>>j & 1 || M[i][j]){ //假如这个位置被用了或者是1 不用填 
                        nex[used]=now[used & ~(1<<j)];  
                    } 
                    else{
                        int res=0;
                        if(j+1<m && !(used>>(j+1)&1) && !M[i][j+1]){ //横着放 
                            res+=now[used|1<<(j+1)];
                        }
                        if(i+1<n && !M[i+1][j] ){ //竖着放 
                            res+=now[used|1<<j];
                        }
                        nex[used]=res%mod;
                    } 
                    
                }
                swap(now,nex);
            }
        }
        cout<<now[0]<<endl;
    }
    return 0; 
}

  

POJ 2441 Arrange the Bulls

题意:有头牛,m个场地,每个牛有各自喜好的场地,把这些牛安排到场地中去,每头牛都要安放在它喜好的场地,有多少种安排方法

思路:压缩dp

#include<bits/stdc++.h>

#define IOS ios_base::sync_with_stdio(0); cin.tie(0)
#define lowbit(x) (x&(-x))
using namespace std;
typedef long long ll;

const double PI=acos(-1.0);
const int INF = 0x3f3f3f3f;
const int MAX_N = 21;
const int MOD = 1000000007;

int dp[1<<MAX_N];
bool chose[MAX_N][MAX_N];

int main(void)
{
    int N,M;
    cin>>N>>M;
    memset(dp,0,sizeof(dp));

    for(int i=1;i<=N;i++)
    {
        int P; cin>>P;
        for(int j=1;j<=P;j++)
        {
            int b; cin>>b;
            chose[i][b]=true;
        }
    }
    dp[0]=1;
    
    for(int i=1;i<=N;i++)
        for(int s=((1<<(M+1))-1);s>=0;s--)
        {
            if(dp[s]){
                for(int j=1;j<=M;j++)
                    if(chose[i][j]&&!(s&(1<<j)))
                      dp[s|(1<<j)]+=dp[s];
            }
            dp[s]=0;
        }
    
    int ans=0;
    for(int s=0;s<(1<<(M+1));s++)
        ans+=dp[s];
    cout<<ans<<endl;

    return 0;
}

  

POJ 3254 Corn Fields

题意:有M×N的玉米地,但其中有些是不肥沃的,不能种植。用1来代表肥沃,0代表不肥沃。另外奶牛不喜欢挨着吃,也就是说要间隔着种植,求有几种种植方式,并将计算结果对1E8取模。

思路:任然是压缩DP的入门题,但和上一题不太一样,但大致思路都是:正在处理的情况受与之相邻的情况的影响,目前看起来对压缩的理解还不是很透彻

#include<bits/stdc++.h>

#define IOS ios_base::sync_with_stdio(0); cin.tie(0)
#define lowbit(x) (x&(-x))
using namespace std;
typedef long long ll;

int N,M,top=0;
int state[600],num[110];
int dp[20][600];
int cur[20];
const int mod = 100000000;

void init()
{
    top=1;
    int line=1<<N;
    for(int i=0;i<line;i++)
        if((i&(i<<1))==0)
            state[top++]=i;
    top--;
    return;
}

int main(void)
{
    while(scanf("%d%d",&M,&N)!=EOF)
    {
        init();
        memset(cur,0,sizeof(cur));
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=M;i++)
        {
            int tem;
            for(int j=1;j<=N;j++)
            {
                scanf("%d",&tem);
                if(tem==0)  cur[i]+=(1<<(N-j));
            }
        }

        for(int i=1;i<=top;i++)
            if(!(state[i]&cur[1]))
                dp[1][i]=1;
        
        for(int i=2;i<=M;++i)
            for(int k=1;k<=top;++k)
                if((state[k]&cur[i])==0)
                    for(int j=1;j<=top;++j)
                        if(((state[j]&cur[i-1])==0)&&((state[j]&state[k])==0))
                            dp[i][k]=(dp[i][k]+dp[i-1][j])%mod;

        int ans=0;
        for(int i=1;i<=top;i++)
            ans=(ans+dp[M][i])%mod;
        printf("%d
",ans);
    }

    return 0;
}

  

POJ 2836 Rectangular Covering

题意:平面上有 n (2 ≤ n ≤ 15) 个点,现用平行于坐标轴的矩形去覆盖所有点,每个矩形至少盖两个点,矩形面积不可为0,求这些矩形的最小面积。

思路:状压DP,dp[S]表示覆盖的点集为S要用的最少矩形面积

#include<bits/stdc++.h>

#define IOS ios_base::sync_with_stdio(0); cin.tie(0)
#define lowbit(x) (x&(-x))
using namespace std;
typedef long long ll;

struct node
{
    int x,y;
}p[16];

int state[200],dp[1<<16],area[200];

int main(void)
{
    int n;
    while(cin>>n&&n)
    {
        for(int i=0;i<n;i++)
            cin>>p[i].x>>p[i].y;
        
        // 枚举n*(n-1)/2个矩形,每个矩形的有自己的状态(覆盖的点)和面积
        int crt=0;
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;j<n;j++)
            {
                state[crt]=(1<<j|1<<i);
                for(int k=0;k<n;k++)
                {
                    if((p[i].x-p[k].x)*(p[k].x-p[j].x)>=0 && (p[i].y-p[k].y)*(p[k].y-p[j].y)>=0)
                    //当一个点在这个矩形中时,把这个矩形的包含点的状态更新
                        state[crt]|=1<<k;
                }
                if(p[i].x==p[j].x)//特判
                    area[crt]=abs(p[i].y-p[j].y);
                else if(p[i].y==p[j].y)
                    area[crt]=abs(p[i].x-p[j].x);
                else
                    area[crt]=abs(p[i].y-p[j].y)*abs(p[i].x-p[j].x);
                crt++;
            }
        }
        memset(dp,INF,sizeof(dp));
        dp[0]=0;
        for(int i=0;i<(1<<n);i++)
            for(int j=0;j<crt;j++)
                // i|state[j] 为第 j 个点加入到枚举情况中的情况
                dp[i|state[j]]=min(dp[i|state[j]],dp[i]+area[j]);
        
        cout<<dp[(1<<n)-1]<<endl;
    }

    return 0;
}

  

POJ 1795 DNA Laboratory

题意:给出N个字符串,求一个最小长度的串,该串包含给出的所有字符串。注意该字符串在长度最小的同时还必须是字典序最小。

这是一道涉及字符串处理的题,首先有很多字符串处理的函数要了解一下

问题:有两个字符串a、b, 现想判断a字符串是否包含b字符串。
>> 先说说string::npos参数: npos 是一个常数,用来表示不存在的位置,类型一般是std::container_type::size_type 许多容器都提供这个东西。
>> 再来说说find函数: find函数的返回值是整数,假如字符串存在包含关系,其返回值必定不等于npos,但如果字符串不存在包含关系,
那么返回值就一定是npos。所以不难想到用if判断语句来实现!
# include <iostream>
# include <string>
using namespace std;

int main() {
    int number;
    cin >> number;
    while (number--) {
        string a, b;
        cin >> a >> b;
        int pos = a.find(b);
        if (pos == string::npos) {
            cout << "NO" << endl;
        }
        else {
            cout << "YES" << endl;
        }
    }
}
substr函数的用法:
0. 用途:一种构造string的方法

1. 形式:s.substr(pos, n)

2. 解释:返回一个string,包含s中从pos开始的n个字符的拷贝(pos的默认值是0,n的默认值是s.size() - pos,即不加参数会默认拷贝整个s)

3. 补充:若pos的值超过了string的大小,则substr函数会抛出一个out_of_range异常;
若pos+n的值超过了string的大小,则substr会调整n的值,只拷贝到string的末尾

  

#include<bits/stdc++.h>

#define lowbit(x) (x&(-x))
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int maxn=17;
int n, dp[maxn][1 << maxn];
string str[maxn], ans;
int cost[maxn][maxn]; //cost[i][j]表示将j字符串

void init()
{
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            if(i != j && str[i].find(str[j]) != string::npos)
                str[j] = str[i];
                // 序列j包含i,只保留母串
    
    stable_sort(str,str+n);
    // sort与stable_sort唯一的差别就是stable_sort是一种稳定的排序,在两个元素相同的时候不交换位置,而sort则不是
    n=unique(str,str+n)-str;
    // 去重

    memset(cost,0,sizeof(cost));
    
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++)
        {
            if(i!=j)
            {
                int len=min(str[i].length(), str[j].length());
                for(int k=0;k<len;k++)
                {
                    if(str[i].substr(str[i].length() - k)==str[j].substr(0, k))
                    {
                        cost[i][j]=str[i].length()-k;
                    }
                }
            }
        }
    }
    return;
}

void dfs(int id, int s)
{
    if(s == 0)   return;
 
    string tmp;
    int next_id=-1;

    for(int i = 0; i < n; i++)
        if(i != id && (s >> i & 1))
        {
            if(dp[id][s] == dp[i][s & ~(1 << id)] + cost[id][i])
            {
                string t = str[i].substr(str[id].length()-cost[id][i], str[i].length());
                if(next_id == -1 || t < tmp)
                {
                    tmp = t;
                    next_id = i;
                }
            }
        }
    ans += tmp;
    dfs(next_id, s & ~(1 << id));
}

int main(void)
{
    int T,kase=0;
    cin>>T;
    while(T--)
    {
        cin>>n;
        for(int i=0;i<n;i++) cin>>str[i];

        if(n<=1)
            ans=str[0];
        else{
            init();
            for(int i=0;i<=n;i++) fill(dp[i],dp[i]+(1<<n),INF);
            for(int i=0;i<n;i++)
                dp[i][1<<i]=str[i].length();

            for(int s = 0; s < 1 << n; s++) {
                for(int j = 0; j < n; j++)
                    if(dp[j][s] != INF && (s >> j & 1)) {
                        for(int i = 0; i < n; i++) 
                            if(!(s >> i & 1)) {
                                dp[i][s|1<<i] = min(dp[i][s|1<<i],dp[j][s]+cost[i][j]);
                            }
                    }
            }

            int id=0;
            for(int i=1;i<n;i++) 
                if(dp[i][(1<<n)-1]<dp[id][(1<<n)-1]) 
                    id = i;
            
            ans=str[id];
            dfs(id,(1<<n)-1);

        }

        cout<<"Scenario #"<<++kase<<":"<<endl;
        cout<<ans<<endl;
        cout<<endl;
    }

    return 0;
}

POJ 3411 Paid Roads

题意:N个城市间有m条单向路,分别从a到b,可以在c处交P路费,也可以直接交R路费。求从1->N 的最小花费,若不能到达,则输出‘impossible

思路:这道题与dij 不同,若是只求能不能到达完全可以用 dij ,但是本题要求最小,每个边和点都可能通过大于1次

#include<bits/stdc++.h>

#define lowbit(x) (x&(-x))
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
int m,n,mcost;

int vis[11];

struct node
{
    int a,b,c,p,r;
}road[11];

void DFS(int a,int fee)
{
    if(a==n&&mcost>fee)
    {
        mcost=fee;
        return;
    }

    for(int i=1;i<=m;i++)
    {
        if(a==road[i].a&&vis[road[i].b]<=3)
        {
            int b=road[i].b;
            vis[b]++;

            if(vis[road[i].c])
                DFS(b,fee+road[i].p);
            else
                DFS(b,fee+road[i].r);

            vis[b]--;
        }
    }
    return;
}

int main(void)
{
    while(cin>>n>>m)
    {
        memset(vis,0,sizeof(vis));
        vis[1]=1;
        mcost=INF;

        for(int i=1;i<=m;i++)
            cin>>road[i].a>>road[i].b>>road[i].c>>road[i].p>>road[i].r;
        
        DFS(1,0);

        if(mcost<INF)
            cout<<mcost<<endl;
        else
            cout<<"impossible"<<endl;   
    }

    return 0;
}
原文地址:https://www.cnblogs.com/jaszzz/p/12986806.html