zoukankan      html  css  js  c++  java
  • Luogu P1074靶形数独【搜索/剪枝】By cellur925

    题目传送门

    显然是一个搜索。但是开始没有任何的剪枝,暴力从$(1,1)$点开始搜索,很自然地T了6个点。

    #include<cstdio>
    #include<algorithm>
    
    using namespace std;
    
    const int group[10][10]=
    {
        0,0,0,0,0,0,0,0,0,0,
        0,1,1,1,2,2,2,3,3,3,
        0,1,1,1,2,2,2,3,3,3,
        0,1,1,1,2,2,2,3,3,3,
        0,4,4,4,5,5,5,6,6,6,
        0,4,4,4,5,5,5,6,6,6,
        0,4,4,4,5,5,5,6,6,6,
        0,7,7,7,8,8,8,9,9,9,
        0,7,7,7,8,8,8,9,9,9,
        0,7,7,7,8,8,8,9,9,9,
    };
    
    const int sco[10][10]=
    {
        0,0,0,0,0,0,0,0,0,0,
        0,6,6,6,6,6,6,6,6,6,
        0,6,7,7,7,7,7,7,7,6,
        0,6,7,8,8,8,8,8,7,6,
        0,6,7,8,9,9,9,8,7,6,
        0,6,7,8,9,10,9,8,7,6,
        0,6,7,8,9,9,9,8,7,6,
        0,6,7,8,8,8,8,8,7,6,
        0,6,7,7,7,7,7,7,7,6,
        0,6,6,6,6,6,6,6,6,6,
    };
    
    int ans,num[50][50];
    bool gong[50][50],hang[50][50],lie[50][50];
    
    void review()
    {
        int val=0;
        for(int i=1;i<=9;i++)
            for(int j=1;j<=9;j++)
                val+=sco[i][j]*num[i][j];
    //	printf("%d
    ",val);
        ans=max(ans,val);
    }
    
    bool check(int x,int y,int w)
    {
        if(gong[group[x][y]][w]||hang[x][w]||lie[y][w]) return 0;
        return 1;
    }
    
    void dfs(int x,int y)
    {
        if(x==10)
        {
            review();
            return ;
        }
        int nx=x,ny=y+1;
        if(ny==10) nx++,ny=1;
        if(num[x][y]) dfs(nx,ny);
        else
        {
            for(int i=1;i<=9;i++)
            {
                if(!check(x,y,i)) continue;
                gong[group[x][y]][i]=1;
                hang[x][i]=1;
                lie[y][i]=1;
                num[x][y]=i;
                dfs(nx,ny);
                gong[group[x][y]][i]=0;
                hang[x][i]=0;
                lie[y][i]=0;
                num[x][y]=0;
            }
        }
    }
    
    int main()
    {
        for(int i=1;i<=9;i++)
            for(int j=1;j<=9;j++)
            {
                int x=0;
                scanf("%d",&x);
                if(!x) continue;
                num[i][j]=x;
                hang[i][x]=1;
                lie[j][x]=1;
                gong[group[i][j]][x]=1;
            }
        dfs(1,1);	
        printf("%d",ans==0 ? -1 : ans);
        return 0;
    }
    

    考虑剪枝。从人类智慧的角度出发,如果是我们玩数独虽然我好像没怎么玩过,我们一定是从填的多的区域出发,因为有更少的决策可供我们选择。那么在这里我们也可以借鉴这个思路,每次统计一下每行每列的有多少数是已经填了的,得到一个最优的坐标,从它出发进行搜索。这个算法保证了我们每次搜的一定是没有填数的,使复杂度优秀了许多。

    #include<cstdio>
    #include<algorithm>
    
    using namespace std;
    
    const int group[10][10]=
    {
    	0,0,0,0,0,0,0,0,0,0,
    	0,1,1,1,2,2,2,3,3,3,
    	0,1,1,1,2,2,2,3,3,3,
    	0,1,1,1,2,2,2,3,3,3,
    	0,4,4,4,5,5,5,6,6,6,
    	0,4,4,4,5,5,5,6,6,6,
    	0,4,4,4,5,5,5,6,6,6,
    	0,7,7,7,8,8,8,9,9,9,
    	0,7,7,7,8,8,8,9,9,9,
    	0,7,7,7,8,8,8,9,9,9,
    };
    
    const int sco[10][10]=
    {
    	0,0,0,0,0,0,0,0,0,0,
    	0,6,6,6,6,6,6,6,6,6,
    	0,6,7,7,7,7,7,7,7,6,
    	0,6,7,8,8,8,8,8,7,6,
    	0,6,7,8,9,9,9,8,7,6,
    	0,6,7,8,9,10,9,8,7,6,
    	0,6,7,8,9,9,9,8,7,6,
    	0,6,7,8,8,8,8,8,7,6,
    	0,6,7,7,7,7,7,7,7,6,
    	0,6,6,6,6,6,6,6,6,6,
    };
    
    int cn,ans,num[50][50];
    int cnt_hang[50],cnt_lie[50];
    bool gong[50][50],hang[50][50],lie[50][50];
    
    void review()
    {
    	int val=0;
    	for(int i=1;i<=9;i++)
    		for(int j=1;j<=9;j++)
    			val+=sco[i][j]*num[i][j];
    //	printf("%d
    ",val);
    	ans=max(ans,val);
    }
    
    bool check(int x,int y,int w)
    {
    	if(gong[group[x][y]][w]||hang[x][w]||lie[y][w]) return 0;
    	return 1;
    }
    
    void dfs(int x,int y,int cnt)
    {
    	if(cnt==81)
    	{
    		review();
    		return ;
    	}
    	for(int i=1;i<=9;i++)
    	{
    		if(!check(x,y,i)) continue;
    		gong[group[x][y]][i]=1;
    		hang[x][i]=1;
    		lie[y][i]=1;
    		num[x][y]=i;
    		cnt_hang[x]++;cnt_lie[y]++;
    		int qwq=-1,qaq=-1,bx=0,by=0;
    		for(int j=1;j<=9;j++)
    			if(cnt_hang[j]>qwq&&cnt_hang[j]<9) 
    				qwq=cnt_hang[j],bx=j;
    		for(int j=1;j<=9;j++)
    			if(cnt_lie[j]>qaq&&(!num[bx][j]))
    				qaq=cnt_lie[j],by=j;
    		dfs(bx,by,cnt+1);
    		gong[group[x][y]][i]=0;
    		hang[x][i]=0;
    		lie[y][i]=0;
    		num[x][y]=0;
    		cnt_hang[x]--;cnt_lie[y]--;
    	}
    }
    
    int main()
    {
    	for(int i=1;i<=9;i++)
    		for(int j=1;j<=9;j++)
    		{
    			int x=0;
    			scanf("%d",&x);
    			if(!x) continue;
    			num[i][j]=x;
    			cnt_hang[i]++;
    			cnt_lie[j]++;
    			hang[i][x]=1;
    			lie[j][x]=1;
    			gong[group[i][j]][x]=1;
    			cn++;
    		}
    	int qwq=-1,qaq=-1,bx=0,by=0;
    	for(int i=1;i<=9;i++)
    		if(cnt_hang[i]>qwq&&cnt_hang[i]<9) 
    			qwq=cnt_hang[i],bx=i;
    	for(int i=1;i<=9;i++)
    		if(cnt_lie[i]>qaq&&(!num[bx][i]))//注意这里找一个没有填数的坐标
    			qaq=cnt_lie[i],by=i;
    	dfs(bx,by,cn);	
    	printf("%d",ans==0 ? -1 : ans);
    	return 0;
    }
    

    Warning

    开始写自己的暴力写法的时候傻了两次:

    ①没输入输出(???)果然T的无可救药

    ②因为我确定宫和价值都是用数组打出表存的,而开始把数组开的很大,$50*50$,但是我们打出的表的部分不是另起一行,编译后会被理解连续的一段,于是我们要限制数组大小,使恰好能够填入数。

  • 相关阅读:
    从零开始的ESP8266探索(1)-使用Server功能搭建Web Server
    模型收集
    3D打印社区
    [教程]教你如何制作彩色的3D打印Groot
    1-5 软件安装
    1-4 打印机测试
    1-3 打印机几个重要操作
    1-1 打印机基本参数
    1-2 打印机使用注意事项
    (二 -3-3) 天猫精灵接入Home Assistant-自动发现Mqtt设备-自动生成配置信息
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9863099.html
Copyright © 2011-2022 走看看