【BZOJ3901】棋盘游戏 局部暴枚取优

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/44793957");
}

~~~

貌似是wyfcyx以前出过的题?

题解:

       那个首先暴力的话并不是 O(217) ,而是 O(21717)
       然后我们分析怎么过此题。

       首先我们发现其实搜索的话,有好几部分都是独立的。
       发现所有的方形都经过中间一行,所以我们可以先 O(217) 枚举中间一行左边一半的取反状态,然后右边的一半可以递推出来,有点像异或前缀和,看代码吧。
       然后对每一列 O(217) 处理,时间复杂度差不多了,但是还是挂的。

       我们发现还能拆。
       我们对每一行进行处理,这样就可以不决定中间一列,只决定中间一点的取反与否。
       然后枚举行内每个点,看它状态,这样把每一行的每一对点的决策最优值加到中间一点取反与否的两个函数值上,最后再把函数值取优返还给这一列的决策(思想上),整列就是当前枚举集合的最优答案。

       所有最优答案中的最优就是全局最优答案。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 35
using namespace std;
int a[N][N],n,m,ans;
bool v[N]; // 表示翻转1,不翻转0
int main()
{
    freopen("test.in","r",stdin);

    int i,j,k;

    scanf("%d",&n),m=n+1>>1;
    for(i=1;i<=n;i++)for(j=1;j<=n;j++)scanf("%d",&a[i][j]);

    for(int S=(1<<m)-1;~S;S--)
    {
        int sum=0;
        for(i=1;i<=m;i++)
        {
            v[i]=(S>>i-1)&1;
            if(v[i])sum-=a[m][i];
            else sum+=a[m][i];
        }
        for(i=m+1;i<=n;i++)
        {
            v[i]=v[m]^v[i-m];
            if(v[i])sum-=a[m][i];
            else sum+=a[m][i];
        }

        for(i=1;i<m;i++)
        {
            int x=-a[i][m],y=a[i][m]; // x翻转,y不翻转
            if(v[m])x+=a[i+m][m],y-=a[i+m][m];
            else x-=a[i+m][m],y+=a[i+m][m];

            for(j=1;j<m;j++)
            {
                int p,q;
                if(v[j])
                {
                    p=-a[i][j]+a[i][j+m]+a[i+m][j];
                    q=-a[i][j]-a[i][j+m]+a[i+m][j];
                    if(v[m])p+=a[i+m][j+m],q-=a[i+m][j+m];
                    else p-=a[i+m][j+m],q+=a[i+m][j+m];
                    p=max(p,-p),q=max(q,-q); // 很巧,,
                }
                else 
                {
                    p=-a[i][j]+a[i][j+m]-a[i+m][j];
                    q=-a[i][j]-a[i][j+m]-a[i+m][j];
                    if(v[m])p-=a[i+m][j+m],q+=a[i+m][j+m];
                    else p+=a[i+m][j+m],q-=a[i+m][j+m];
                    p=max(p,-p),q=max(q,-q);
                }
                x+=p,y+=q;
            }
            sum+=max(x,y);
        }
        ans=max(ans,sum);
    }
    cout<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值