• 状压$DP$练习


    一道状压DP好题;

    思路出来有点有意思:是树型加状压的转移。

    将分部压掉就可以了

    但我是挂在了初始化上……

    (QAQ)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
        int f=1,w=0;char x=0;
        while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
        while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
        return w*f;
    }
    const int N=101;
    int n,m,t;
    int val[4100],cost[101][20];
    int head[4*N],num_edge,dp[401][4100];
    struct Edge{int next,to;} edge[4*N];
    inline void add(int from,int to)
    {
        edge[++num_edge].next=head[from];
        edge[num_edge].to=to;
        head[from]=num_edge;
    }
    inline void dfs(int pos,int fa)
    {
        for(int i=head[pos];i;i=edge[i].next)
            if(edge[i].to!=fa)
            {
                dfs(edge[i].to,pos);
                for(int j=(1<<m)-1;j;j--)
                    for(int k=j;k;k=(k-1)&j)
                        dp[pos][j]=max(dp[pos][j],dp[pos][j^k]+dp[edge[i].to][k]);
            }
        for(int i=(1<<m)-1;i;i--) dp[pos][i]+=val[i];
    }
    int main(){
        n=read(),m=read();
        //memset(head,-1,sizeof(head)),num_edge=-1;
        for(int i=1;i<n;i++)
        {
            int u=read(),v=read();
            add(u,v),add(v,u);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<m;j++)
                cost[i][j]=read();
            dp[i][0]=0;
            for(int j=1;j<(1<<m);j++)
            {
                int lowbit=j&(-j),lownum=(log(lowbit)+0.0001)/(log(2));
                dp[i][j]=dp[i][j^lowbit]-cost[i][lownum];
            }
        }
        t=read();
        for(int i=1;i<=t;i++)
        {
            int v=read(),cnt=read(),s=0;
            for(int j=1;j<=cnt;j++)
            {
                int part=read();
                s|=(1<<(part-1));
            }
            val[s]+=v;
            int tot=(1<<m)-1,tmp=s^tot;
            for(int j=tmp;j;j=(j-1)&tmp) val[(s|j)]+=v;
        }
        dfs(1,0);
        printf("%d",dp[1][(1<<m)-1]);
    }
    

    现在才发现,状压(DP)的状态很考思维,即你的状态和你的转移有很大关系。

    如果,你的状态压的好,就会很简化方程,相反如果你的状态一般,很有可能不太好转移甚至是无法转移!

    例题:(PO2411)

    这一题的状态很有意思,其实一开始我想的是横着放的标为(0),竖着放的标为(1)

    但是有一个极为明显的问题:

    像这样的状态,就会有无法判定当前行和上一行的关系,状态不能对应(相当于是无法判定竖着放的是否残缺)

    因此就有这样的状态,很有意思:横着放的为(1),而竖着放的上(0)(1),这样就很巧妙的避免了上面的问题。

    相当于我们用(1)来判定了当前位置是否为一个结束。

    那么转移就很好办了,状压的转移一般只要手玩一下就可以了吧(也许是我还没做到这样的毒瘤题?(QwQ)

    嘿呀,上代码:

    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>//忍不住要吐槽poj的头文件,真的恶心哈
    using namespace std;
    inline int read()
    {
        int f=1,w=0;char x=0;
        while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
        while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
        return w*f;
    }
    int n,m;
    long long f[15][1<<12];
    inline bool FirLin(int s)
    {
    	for(int i=0;i<m;)
    		if(s&(1<<i))
    		{
    			if(i==m-1) return 0;
    			if(s&(1<<i+1)) i+=2;
    			else return 0;
    		}
    		else i++;
    	return 1;
    }
    inline bool judge(int now,int pre)
    {
    	for(int i=0;i<m;)
    		if(now&(1<<i))
    			if(pre&(1<<i))
    				if(i!=m-1&&(now&(1<<i+1))&&(pre&(1<<i+1))) i+=2;
    				else return 0;
    			else i++;
    		else if(pre&(1<<i)) i++;
    		else return 0;
    	return 1;
    }
    int main(){
    	while(scanf("%d%d",&n,&m)&&n&&m)
    	{
    		if(n<m) swap(n,m);
    		memset(f,0,sizeof(f));
    		for(int i=0;i<(1<<m);i++) f[1][i]=FirLin(i)?1:0;
    		for(int i=2;i<=n;i++)
    			for(int j=0;j<(1<<m);j++)
    				for(int k=0;k<(1<<m);k++)
    					if(judge(j,k)) f[i][j]+=f[i-1][k];
    		printf("%lld
    ",f[n][(1<<m)-1]);
    	}
    }
    

    下一个:一个三进制的状压(DP),说实话,我以前的思维被二进制的状压禁锢,应该说没有考虑过其他进制。

    例题:(POJ1038)

    观察可得,显然当前行的状态与前两行的状态有关系,不妨将前两行压在一起,用一个三进制表示,具体的压法为:

    (0)表示(i)(i-1)行都没有放;(1)表示(i)行没放(i-1)行放了;(2)表示(i)(i-1)行都放了

    转移时分个类,讨个论即可:

    上代码了呦!

    //0表示i和i-1行都没有放//1表示i行没放i-1行放了//2表示i和i-1行都放了
    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>//忍不住要吐槽poj的头文件,真的恶心哈
    using namespace std;
    inline int read()
    {
        int f=1,w=0;char x=0;
        while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
        while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
        return w*f;
    }
    int n,m,k,las,now,c;
    int a[200][30],pw[20],f[2][60000];
    inline int Las(int i) {return las/pw[i-1]%3;}
    inline int Now(int i) {return now/pw[i-1]%3;}
    inline void Dfs(int Lin,int cnt)//在基础状态上跑出所有可能的放法
    {
    	if(Lin>m) return ;
    	if(Lin<m&&!Las(Lin)&&!Las(Lin+1)&&!Now(Lin)&&!Now(Lin+1))
    	{
    		now+=(pw[Lin-1]+pw[Lin])*2;f[c][now]=max(f[c][now],cnt+1);
    		Dfs(Lin+2,cnt+1),now-=(pw[Lin-1]+pw[Lin])*2;
    	}
    	if(Lin<m-1&&!Now(Lin)&&!Now(Lin+1)&&!Now(Lin+2))
    	{
    		now+=(pw[Lin-1]+pw[Lin]+pw[Lin+1])*2;f[c][now]=max(f[c][now],cnt+1);
    		Dfs(Lin+3,cnt+1),now-=(pw[Lin-1]+pw[Lin]+pw[Lin+1])*2;
    	}
    	Dfs(Lin+1,cnt);
    }
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("Text1.in","r",stdin);
    #endif
    	int t=read();pw[0]=1;
    	for(int i=1;i<11;i++) pw[i]=pw[i-1]*3;
    	while(t--)
    	{
    		n=read(),m=read(),k=read(),las=0;
    		memset(a,0,sizeof(a));
    		memset(f[0],-1,sizeof(f[0]));
    		for(int i=1,u,v;i<=k;i++) u=read(),v=read(),a[u][v]=1;
    		for(int i=1;i<=m;i++) las+=pw[i-1]*(a[1][i]+1);
    		f[0][las]=0,c=0;int ans=0;
    		for(int i=2;i<=n;i++)
    		{
    			c^=1;
    			memset(f[c],-1,sizeof(f[c]));
    			for(las=0;las<pw[m];las++)
    				if(~f[c^1][las])
    				{
    					now=0;
    					for(int j=1;j<=m;j++)
    						if(a[i][j]) now+=pw[j-1]*2;
    						else now+=pw[j-1]*(Las(j)?Las(j)-1:0);//跑出对应基础状态(即啥也不放的必须状态)
    					f[c][now]=max(f[c][now],f[c^1][las]),Dfs(1,f[c^1][las]);
    				}
    		}
    		for(int i=0;i<pw[m];i++) ans=max(ans,f[c][i]);
    		printf("%d
    ",ans);
    	}
    }
    
    
  • 相关阅读:
    ping
    android Handler总结
    配置网络测试环境的批处理
    shell 的选项解析
    [转] Linux 中共享库的搜索
    SecureCRT 脚本一则(0720.Rev.1)
    使用 Wget 完成自动 Web 认证(推 portal)
    shell 选项解析之需求一:多路径自动补全
    getopts 的简单模拟(09.12 Rev)
    ThinkPHP框架使用心得三 RBAC权限控制(2)简要原理
  • 原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/10975497.html
Copyright © 2020-2023  润新知