1 条题解

  • 0
    @ 2026-4-5 1:38:08
    /*
    每一行有n个数,第i个数选为1不选为0,假设n=5,其中一个合法的方案如下:
    1 0 0 0 1
    0 0 1 0 0
    1 0 0 0 0
    0 0 1 0 1
    1 0 0 0 0
    
    如果表达以上状态,很容易想到设计状态如下:
    bool f[6][2][2][2][2][2]
    但是n不确定,定义也不能确定(状态推导的代码也不好写) 
    
    正确的做法是用一个整数(它的二进制形式)表示一行的01串,具体如下:
    int f[6][32]; 
    1 0 0 0 1=17 -> f[1][17] 
    0 0 1 0 0=4  -> f[2][4]
    1 0 0 0 0=16 -> f[3][16]
    0 0 1 0 1=5  -> f[4][5]
    1 0 0 0 0=16 -> f[5][16]
    算法过程: 
    1、保存所有合法状态v存于s数组中。
    状态v为合法状态的条件:就是v的二进制表示形式中不能有连续2个1 
    [v&(v<<1)==0] && [v&(v>>1)==0] 
    比如:n=7,v=37,
    v的二进制是       0100101,
    v<<1的二进制是:  1001010,
    v&(v<<1)的结果是:--------
                      0000000
    说明v没有连续的2个1,才能使得 v&(v<<1)的结果等于0 
    
    v的二进制是       0100101,
    v>>1的二进制是:  0010010,
    v&(v>>1)的结果是:--------
                      0000000 
    也能说明v没有连续的2个1,才能使得 v&(v<<1)的结果等于0 
    2、f[i][v]:表示只考虑第1-第i行,且第i行的状态是v时能合法获取的最大值。
    3、f[i][v]如何衔接f[i-1][x]?x是第i-1行的状态值。要想f[i]能继承f[i-1],必须v和x不冲突:
    [v&x==0]  && [v&(x>>1)==0] && [v&(x<<1)==0]
    */
    
    #include<bits/stdc++.h>
    using namespace std;
    int a[20][20];
    int slen,s[1<<15];
    int f[20][1<<15];
    
    int main()
    {
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&a[i][j]);
        
        slen=0;
        for(int v=0;v<(1<<n);v++) if(  !(v&(v>>1))  &&  !(v&(v<<1))   )s[++slen]=v;//保存合法状态 
    
        memset(f,0,sizeof(f));
        for(int i=1;i<=n;i++)
    		for(int j=1;j<=slen;j++)
    		{
    			int t=0;for(int k=0;k<n;k++) if( (1<<k) & s[j] ) t+=a[i][k+1];
    
    			for(int k=1;k<=slen;k++)
    				if( !(s[j] & (s[k]>>1)) && !(s[j] & (s[k]<<1)) && !(s[j]&s[k]) )
    					f[i][s[j]]=max(f[i][s[j]],t+f[i-1][s[k]]);
    		}
        int ans=0;for(int i=1;i<=slen;i++)ans=max(ans,f[n][s[i]]);
        printf("%d\n",ans);
        return 0;
    }
    
    • 1

    信息

    ID
    537
    时间
    1000ms
    内存
    16MiB
    难度
    6
    标签
    递交数
    199
    已通过
    62
    上传者