• [USACO11NOV]二进制数独Binary Sudoku


    传送门

    这道题是很好的一道IDA*练习题。

    首先我们先确定搜索的框架,我们要求的是用最少的修改次数使得所有的行,列,宫之内都有偶数个1,最直观的想法显然是先预处理出有奇数个1的行,列,宫,之后枚举每一个点,如果这个点在奇数个1的行/列/宫之中就开始修改,继续搜索。修改整个数独之后判定,回溯。

    这个正确性是没问题的,但是你难以通过有效的手段减少搜索树大小(比如从1多的开始搜?),这样肯定会超时(想想靶型数独)。

    于是我们引入新的操作:ID(迭代加深)搜索!

    ID的原理是,有可能搜索树某些分枝非常的深,但里面并没有你要的解,如果你率先进入的话,就会浪费大量的时间在里面。有可能解的深度并不是很大,所以我们可以每次设定一个搜索的深度,如果超过这个深度就返回,之后继续加大深度搜索,所以叫迭代加深。

    但是这样我们依然可能很慢,所以我们再加上一个,A*算法!

    A*算法简单来说,就是设计一个估价函数,估计未来的搜索情况,选择当前估计最好的一个搜索方向去搜索。估计的值越接近实际值,搜索效率越高。估价函数有一个原则,就是估计的值不可以大于实际值,否则就会导致得到错误的答案。(因为估计值过大掩盖了真实的解)而且我们无需担心正确性,因为A*算法只是优先进入分支,在进行一段时间搜索之后,原来被忽略的分支又将进入计算。A*的估价函数如果一直是0,那么他就相当于是一个无优化的搜索,所以说A*算法的效率一般来讲是高于普通搜索的,至于高多少就要看设计的估价函数的高明之处了。

    A*的实现比较复杂,但与ID结合起来成为IDA*就比较好实现了,它其实可以被视为剪枝,当当前的值加上估价值比当前的最大深度更大,就直接返回,相当于剪枝了。在这道题中,我们要做的就是从小到大枚举搜索深度(就是修改的次数),在每次设计一个估价函数,根据上面的原则,我们修改之后,至多需要max有奇数个1的(行,列,宫)次修改,每次估价函数设为三者最大值即可。

    这样搜索快到飞起,也有可能是本题数据比较水……

    看一下代码。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cmath>
    #include<set>
    #include<queue>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    typedef long long ll;
    const int M = 10005;
    const int INF = 1000000009;
    
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
        if(ch == '-') op = -1;
        ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
        }
        return ans * op;
    }
    
    int b,c,r,row[11],col[11],blo[11],maxd;
    char s[11];
    
    int B(int x,int y)
    {
       return ((x - 1) / 3 * 3 + (y - 1) / 3) + 1;
    }
    
    int h()
    {
       return max(r,max(b,c));
    }
    
    int cal(int x)
    {
       return (!x) ? -1 : 1;
    }
    
    void change(int i,int j)
    {
       row[i] ^= 1,col[j] ^= 1,blo[B(i,j)] ^= 1;
       r += cal(row[i]),c += cal(col[j]),b += cal(blo[B(i,j)]);
    }
    
    void dfs(int x,int y,int d)
    {
       if(d > maxd) return;
       if(x == 9 && y == 9)
       {
          rep(i,1,9) if(row[i] | col[i] | blo[i]) return;
          printf("%d
    ",maxd),exit(0);
       }
       if(row[x] | col[y] | blo[B(x,y)])
       {
          change(x,y);
          if(d + 1 + h() <= maxd) (y == 9) ? dfs(x+1,1,d+1) : dfs(x,y+1,d+1);
          change(x,y);
          if(d + h() <= maxd) (y == 9) ? dfs(x+1,1,d) : dfs(x,y+1,d);
       }
       else (y == 9) ? dfs(x+1,1,d) : dfs(x,y+1,d);
    }
    
    int main()
    {
       rep(i,1,9)
       {
          scanf("%s",s+1);
          rep(j,1,9) if(s[j] == '1') change(i,j);
       }
       for(maxd = 0;;maxd++) dfs(1,1,0);
       return 0;
    }
       
  • 相关阅读:
    MySQL插入数据获得自动增长的ID
    C#解决“System.Threading.ThreadStateException:“在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。请确保您的 Main 函数带有 STAThreadAttribute 标记。 只有将调试程序附加到该进程才会引发此异常”的异常
    C# 关闭当前窗体的时候打开另一个窗口
    C# WinForm中关闭指定的窗体
    MySQL 8.0 版本修改字符编码
    微信支付API v3接口使用应用篇
    Linux Tomcat安装篇(daemon运行,开机自启动)
    Linux nginx安装篇
    SpringCloud系列之Nacos+Dubbo+Seata应用篇
    SpringCloud系列之Nacos+Dubbo应用篇
  • 原文地址:https://www.cnblogs.com/captain1/p/9886229.html
Copyright © 2020-2023  润新知