状压dp初探

写了几道状压。。。然后就一直在颓废。。。

2064: 分裂

http://www.lydsy.com/JudgeOnline/problem.php?id=2064

初始的为正,最后的为负,假设我们能找到k组凑成0的话,答案就是n+m-2*k。于是状压。。其实我一点都不会。。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define maxn 1<<22
using namespace std;
int t,mm,f[maxn],sum[maxn],ans,n,m,a[50],b[50];
int main(){
    scanf("%d",&n);
    rep(i,0,n-1) scanf("%d",&a[i]);
    scanf("%d",&m);
    rep(i,0,m-1) scanf("%d",&b[i]);
    rep(i,0,n-1) sum[1<<i]=a[i];
    rep(i,n,n+m-1) sum[1<<i]=-b[i-n];
    mm=1<<(n+m);
    rep(i,1,mm-1){
        t=i&(-i);
        sum[i]=sum[t]+sum[i-t];
        rep(j,0,m+n-1)
            if ((1<<j)&i) f[i]=max(f[i],f[i^(1<<j)]);
        if (!sum[i]) ++f[i];   
    }
    printf("%d\n",n+m-2*f[mm-1]);
    return 0;
}
View Code
1725: [Usaco2006 Nov]Corn Fields牧场的安排
http://www.lydsy.com/JudgeOnline/problem.php?id=1725
f[i][j]表示第i行,情况是j的答案。于是裸状压。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define maxn 5000
#define mm 100000000
int y,sum,tot,ans,ff[20][maxn],f[20][maxn],g[20][maxn],d[maxn],map[20][20],n,m;
using namespace std;
void dfs(int x){
    if (x>m) {
        d[y]=++tot;
        g[y][tot]=sum;
        ff[y][tot]=ans;
        return;
    }
    if (map[y][x]==1){
        sum+=(1<<(x-1));
        ans++;
        dfs(x+2);
        sum-=(1<<(x-1));
        ans--;
    }
    dfs(x+1);
}
int main(){
    scanf("%d%d",&n,&m);
    rep(i,1,n)
        rep(j,1,m) scanf("%d",&map[i][j]);
    rep(i,1,n){
        tot=ans=sum=0;
        y=i;
        dfs(1);
    }
    ans=0;
    if (n==1) printf("%d\n",d[1]);
    else {
        rep(i,1,d[1]) f[1][i]=1;
        rep(i,2,n)
            rep(j,1,d[i])
                rep(k,1,d[i-1]){
                    if ((g[i][j]&g[i-1][k])==0) f[i][j]=(f[i][j]+f[i-1][k])%mm;
                }  
        rep(i,1,d[n]) ans=(ans+f[n][i])%mm;
        printf("%d",ans);
    }
    return 0;
}
View Code

 

1231: [Usaco2008 Nov]mixup2 混乱的奶牛
http://www.lydsy.com/JudgeOnline/problem.php?id=1231
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define ll long long
#define maxn 70000
ll f[maxn][20],mm,ans;
int n,m,a[20];
int main(){
    scanf("%d%d",&n,&m);
    rep(i,1,n) scanf("%d",&a[i]);
    rep(i,0,n-1) f[1<<i][i+1]=1;
    mm=(1<<n)-1;
    rep(i,0,mm)
        rep(j,1,n) if (1<<(j-1)&i)
            rep(k,1,n) if ((1<<(k-1)|i)!=i&&abs(a[k]-a[j])>m) f[i|1<<(k-1)][k]+=f[i][j];
    rep(i,1,n) ans+=f[mm][i];  
    printf("%lld\n",ans);
    return 0;
}
View Code

 

2073: [POI2004]PRZ
http://www.lydsy.com/JudgeOnline/problem.php?id=2073
主要就是枚举子集转移的时候比较神奇。其实也没什么。。。
(这个比较神奇是这样的,每次&(j-1)可以发现每次去掉了原二进制数中最后一个1,也就是砍掉了这个人。这样的好处在于保证正确性然后。。因为答案是多个集合的答案加起来,如果我从头枚举转移的子集,那时间复杂度是不允许的,但是用上面那种方式转移的话,转移的时间复杂度就只有n*2^n
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define rep(i,l,r) for (int i=l;i<=r;i++)
#define ll long long
#define maxn 70000
using namespace std;
int mm,n,m,a[20],b[20],t[20],w[maxn],tim[maxn],f[maxn];
int main(){
    scanf("%d%d",&m,&n);
    rep(i,1,n) scanf("%d%d",&a[i],&b[i]),t[i]=1<<(i-1);
    mm=(1<<n)-1;
    rep(i,0,mm)
        rep(j,1,n) if (t[j]&i) tim[i]=max(tim[i],a[j]),w[i]+=b[j];
    memset(f,127/3,sizeof(f)); f[0]=0;
    rep(i,1,mm)
        for(int j=i;j;j=i&(j-1))
            if (w[j]<=m) f[i]=min(f[i],tim[j]+f[i^j]);
    printf("%d\n",f[mm]);
    return 0;
}
View Code

 

 

posted on 2015-07-12 23:27  ctlchild  阅读(187)  评论(0编辑  收藏  举报

导航