• 状压dp


    因为有道ac自动机+状压dp的题,故开此坑。

    状压dp:状态压缩dp(元素数量通常不超过20),借助位运算将状态压缩。

    空间复杂度:O(n*n)

    上一行的状态为now,下一行的状态为prev,通过枚举上一行所有状态,来更新当前行、当前状态的最优解。

    给定n*m矩阵,行列都不超过20,有些格子可选有些不可选,需选出最多格子使得格子之间不相邻(无公共边)

    #include <iostream>
    #include <string.h>
    using namespace std;
    const int MAX_N=20;
    const int MAX_M=20;
    int state[MAX_N+1];
    int dp[MAX_N+1][ 1 <<MAX_M];
    bool not_intersect(int now,int prev)
    {
        return (now&prev)==0;
    }
    bool fit(int now,int flag)
    {
        return(now|flag)==flag;
    }
    bool ok(int x)
    {
        return(x&(x/2))==0;
    }
    int count(int now)
    {
        int s=0;
        while(now)
        {
            s+=(now&1);
            now>>=1;
        }
        return s;
    }
    int main() 
    {
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<m;j++)
            {
                int flag;
                cin>>flag;
                state[i]|=(1<<j)*flag;
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<(1<<m);j++)
            {
                if(!ok(j)||!fit(j,state[i]))
                {continue;}
                int cnt=count(j);
                for(int k=0;k<(1<<m);k++)
                {
                    if(ok(k)&&fit(k,state[i-1])&&not_intersect(j,k))
                       {
                           dp[i][j]=max(dp[i][j],dp[i-1][k]+cnt);
                       }
                }
            }
        }
        
        int ans=0;
        for(int i=0;i<(1<<m);i++)
        {
            ans=max(ans,dp[n][i]);
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    长度不超过16的字符串S,如果一个子序列是回文串,即可以移除它,求最少经过多少步能移除整个串。

    序列回文?1:min(子集a次数,a的互补子集b次数),时间复杂度O(3n+n*2n

    枚举子集代码

    for (int t = 1; t < (1 << n); t++) {  // 枚举当前状态
         dp[t] = IsPalindrome(t) ? 1 : inf;  // 判断当前状态是否是回文,如果是回文则步骤数为 1
        for(int i = t; i; i = (i - 1) & t) { // 枚举 t 的所有子集
            dp[t] = min(dp[t], dp[i] + dp[t ^ i]);  // 更新当前状态的解的最小值
        }
    }
    printf("%d
    ", dp[(1 << n) - 1]);  // 输出最终答案
    View Code

    完整代码

    #include<bits/stdc++.h>
    using namespace std;
    const int inf=0x3f3f3f3f;
    char s[20];
    int n,dp[1<<17];
    bool ispa(int now)
    {
        string str="";
        for(int i=0;i<n;i++)
        {
            if((now&(1<<i))==(1<<i))
            {
                str+=s[i];
            }
        }
        string str2=str;
        reverse(str2.begin(),str2.end());
        return str2==str;
    }
    
    int main()
    {
        scanf("%s",s);
        n=strlen(s);
        
        for(int t=1;t<(1<<n);t++)
        {
            dp[t]=ispa(t)?1:inf;
            for(int i=t;i;i=(i-1)&t)
            {
                dp[t]=min(dp[t],dp[i]+dp[t^i]);
            }
        }
        
        int ans=dp[(1<<n)-1];
        cout<<ans<<endl; 
        return 0;
    }
    View Code

    ↓一道差不多的题:https://www.luogu.org/problem/P2704

    灌溉机器人,一个P只影响得到四个方向八个点,所以第r行用r-1行和r-2行来推,每一行状态推一下,同行不互斥的状态有60种,滚动数组

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100;
    const int maxm=10;
    int state[maxn+1],cnt,stateline[maxn+1],sum[maxn+1];
    int dp[maxn+1][65][65];
    
    bool fit(int now,int flag)
    {
        return (now|flag)==flag;
    }
    bool ok(int x)
    {
        return (x&(x/2))==0&&(x&(x/4))==0;
    }
    int count(int now)
    {
        int s=0;
        while(now)
        {
            s+=(now&1);
            now>>=1;
        }
        return s;
    }
    
    int main()
    {
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<m;j++)
            {
                char flag;
                cin>>flag;
                int tmp=flag=='P'?1:0;
                state[i]|=(1<<j)*tmp;
            }
        }
        for(int i=0;i<(1<<m);i++)
        {
            if(ok(i))
            {
                cnt++;
                stateline[cnt]=i;
                sum[cnt]=count(i);
            }
        }
    
        for(int i=1;i<=cnt;i++)
        {
            if(fit(stateline[i],state[1]))
            dp[1][1][i]=sum[i];
        }
    
        for(int r=2;r<=n;r++)
        {
            for(int i=1;i<=cnt;i++)
            {
                if(!fit(stateline[i],state[r]))continue;
                for(int j=1;j<=cnt;j++)
                {
                    if(stateline[i]&stateline[j])continue;
                    for(int k=1;k<=cnt;k++)
                    {
                        if(stateline[i]&stateline[k])continue;
                        dp[r][j][i]=max(dp[r][j][i],dp[r-1][k][j]+sum[i]);
                    }
                }
            }
        }
        int ans=0;
        for(int i=1;i<=cnt;i++)
        {
            for(int j=1;j<=cnt;j++)
            {
                ans=max(ans,dp[n][i][j]);
            }
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    n辆小车(物资)用一辆船运,每次运的车总重不能超过wei,每辆车重w[i],对船损坏值s[i],每次运输对船的损坏值为max 船上所有车损坏值,问运完所有车的min总共损坏值,枚举子集

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=400;
    const int maxm=16+1;
    const int inf=0x3f3f3f3f;
    int s[maxm+1],w[maxm+1];
    int dp[1<<maxm];
    int wei,n;
    
    bool ok(int now)
    {
        int ans=0;
        for(int i=0;i<n;i++)
        {
            if((now&(1<<i))==(1<<i))
            {
                ans+=w[i+1];
                if(ans>wei)
                return false;
            }
        }
        return true;
    }
    
    int count(int now)
    {
        int ans=0;
        for(int i=0;i<n;i++)
        {
            if((now&(1<<i))==(1<<i))
            ans=max(ans,s[i+1]);
        }
        return ans;
    }
    
    
    int main()
    {
        scanf("%d%d",&wei,&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&s[i],&w[i]);
        }
    
        for(int t=1;t<(1<<n);t++)
        {
            if(ok(t)){dp[t]=count(t);continue;}
            else dp[t]=inf;
            for(int i=t;i;i=(i-1)&t)
            {
                dp[t]=min(dp[t],dp[i]+dp[t^i]);
            }
        }
        
        int ans=dp[(1<<n)-1];
        cout<<ans<<endl;
    
        return 0;
    }
    View Code

    n个点,有2n-1种状态(1到(1<<n)-1),给点填色,有线相连的点不能填同一个颜色,si表示状态i填色需要的最少颜色种类,求。枚举的时候枚举一下是否能用一个颜色就可以了。

    如果mod=2^32,可以定义unsigned int,使其自然溢出。

    #include<bits/stdc++.h>
    using namespace std;
    #define ull unsigned long long
    const int inf=0x3f3f3f3f;
    int n,dp[1<<16];
    char ch[20][20];
    unsigned int mod=1ll<<32; 
    bool isone(int now)
    {
        bool ok[20]={false}; 
        for(int i=0;i<n;i++)
        {
            if(now&(1<<i))
            {
                ok[i+1]=true;
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(ch[i][j]=='1'&&ok[i]&&ok[j])
                return false;
            }
        }
        return true;
    }
    
    
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                cin>>ch[i][j];
            }
        }
        
        unsigned int ans=0,c=233;
        for(int t=1;t<(1<<n);t++,c*=233)
        {
            if(isone(t))dp[t]=1;
            else 
            {
                dp[t]=inf;
                for(int i=t;i;i=(i-1)&t)
                {
                    dp[t]=min(dp[t],dp[i]+dp[t^i]);
                }
            }
            ans+=dp[t]*c;
        }
    
        cout<<ans<<endl;
        return 0;
    }
    View Code

    暂时完结...

  • 相关阅读:
    android面试之怎么把图片变成圆形
    android面试之contentProvider获取联系人
    Android面试之assets和aw文件的使用
    Android设计模式之面试
    Activity、Window、View的关系
    ViewPager的简单用法
    补间动画
    帧动画
    android系统的样式和主题
    C++的三种继承方式简述
  • 原文地址:https://www.cnblogs.com/myrtle/p/11441144.html
Copyright © 2020-2023  润新知