zoukankan      html  css  js  c++  java
  • codevs1174 靶形数独(DLX)

    题目描述 Description

    小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他
    们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向Z 博士请教,
    Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。
    靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有9 个3 格宽×3 格
    高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些

    数字,利用逻辑推理,在其他的空格上填入1 到9 的数字。每个数字在每个小九宫格内不能
    重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即
    每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。

     

    上图具体的分值分布是:最里面一格(黄色区域)为 10 分,黄色区域外面的一圈(红
    色区域)每个格子为9 分,再外面一圈(蓝色区域)每个格子为8 分,蓝色区域外面一圈(棕
    色区域)每个格子为7 分,最外面一圈(白色区域)每个格子为6 分,如上图所示。比赛的
    要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取
    更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字
    的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为2829。游
    戏规定,将以总分数的高低决出胜负。

    由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能
    够得到的最高分数。

    输入描述 Input Description

    一共 9 行。每行9 个整数(每个数都在0—9 的范围内),表示一个尚未填满的数独方
    格,未填的空格用“0”表示。每两个数字之间用一个空格隔开。

    输出描述 Output Description

    输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数-1。

    样例输入 Sample Input

    【输入输出样例 1】

    7 0 0 9 0 0 0 0 1
    1 0 0 0 0 5 9 0 0
    0 0 0 2 0 0 0 8 0
    0 0 5 0 2 0 0 0 3
    0 0 0 0 0 0 6 4 8
    4 1 3 0 0 0 0 0 0
    0 0 7 0 0 2 0 9 0
    2 0 1 0 6 0 8 0 4
    0 8 0 5 0 4 0 1 2

    【输入输出样例 2】

    0 0 0 7 0 2 4 5 3
    9 0 0 0 0 8 0 0 0
    7 4 0 0 0 5 0 1 0
    1 9 5 0 8 0 0 0 0
    0 7 0 0 0 0 0 2 5
    0 3 0 5 7 9 1 0 8
    0 0 0 6 0 1 0 0 0
    0 6 0 9 0 0 0 0 1
    0 0 0 0 0 0 0 0 6

    样例输出 Sample Output

    【输入输出样例 1】

    2829

    【输入输出样例 1】

    2852

    数据范围及提示 Data Size & Hint

    【数据范围】
    40%的数据,数独中非0 数的个数不少于30。
    80%的数据,数独中非0 数的个数不少于26。
    100%的数据,数独中非0 数的个数不少于24。

    貌似没有别的办法,只能搜索所有的解,然后取权值最大的一个。

    这题我直接dfs只能过80%的数据。

    数独属于精确覆盖问题,DLX算法是目前已知求解数独的最有效的方法之一。

     

    关于DLX请看这里

     

    我用的是刘汝佳《算法竞赛入门经典:训练指南》上给出的代码。

    DLX算法中,

    行代表决策,因为要选一些行来覆盖所有的列。每个决策用一个三元组(r,c,v)表示,意思是将(r,c)格子填上字母v,因此对于9*9的数独一共有9*9*9=729行。

    列代表任务,因为要让每列恰好有一个“1”,即每列都被覆盖。一共有以下四种任务需要达成:

    Slot(a,b)表示第a行第b列要有数字

    Row(a,b)表示第a行要有数字b

    Col(a,b)表示第a列要有数字b

    Sub(a,b)表示第a个子方阵要有字母b

    因此一共9*9*4=324列。

    DLX矩阵中的“1”代表对应的决策达成了对应的任务。上面已经将决策写成了三元组(r,c,v)的形式,它达成了四个任务:Slot(r,c),Row(r,v),Col(c,v),Sub(Prc,k),其中Prc表示第r行c列所在子方阵编号。不难算出一共有729*4=2916个结点。


    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<cassert>
    #include<set>
    using namespace std;
    #define debug(x) cout<<"debug  "<<x<<endl;
    //*****************************************************
    const int maxnode = 20000;
    const int maxn = 20000;
    #define rep(i,A,s) for(int i = A[s]; i != s; i = A[i])
    
    
    const int SLOT = 0;
    const int ROW = 1;
    const int COL = 2;
    const int SUB = 3;
    int puzzle[10][10];
    
    
    int encode(int a,int b,int c){
        return (a*81) + (b*9) + c + 1;
    }
    void decode(int code,int &a,int &b,int &c){
    	--code;
    	c = code % 9; code /= 9;
    	b = code % 9; code /= 9;
    	a = code;
    }
    
    struct DLX
    {
    	int n, sz;
    	int S[maxn];
    	int row[maxnode],col[maxnode];
    	int L[maxnode],R[maxnode],U[maxnode],D[maxnode];
    
    	int ansd , ans[maxn];
    	int sc[10][10];
        void pre()
        {
            for(int s = 0; s <= 4; ++s){
                for(int i = s, j=s; j <= 8-s; ++j)sc[i][j] = s+6;
                for(int i = s+1; i <= 8-s-1; ++i)
                    for(int j = s; j <= 8-s; j += 8-s*2)sc[i][j] = s+6;
                for(int i = 8-s, j = s; j <= 8-s; ++j)sc[i][j] = s+6;
            }
        }
    	void init(int n){
    		this->n = n;
    		for(int i = 0; i <= n; ++i){
    			U[i] = D[i] = i;
    			L[i] = i-1;
    			R[i] = i+1;
    		}
    		R[n] = 0;L[0] = n;
    		sz = n+1;
    		memset(S,0,sizeof(S));
    	}
    
    	void addRow(int r,const vector<int>& cols){
    		int first = sz;
    		for(int i = 0; i < cols.size(); ++i){
    			int c = cols[i];
    			L[sz] = sz - 1; R[sz] = sz + 1; D[sz] = c; U[sz] = U[c];
    			D[U[c]] = sz; U[c] = sz;
    			row[sz] = r; col[sz] = c;
    			++S[c];
    			++sz;
    		}
    		R[sz - 1] = first; L[first]  = sz - 1;
    	}
    
    	void remove(int c){
    		L[R[c]] = L[c];
    		R[L[c]] = R[c];
    		rep(i,D,c)rep(j,R,i){
    			U[D[j]] = U[j];	D[U[j]] = D[j]; --S[col[j]];
    		}
    	}
    	void restore(int c){
    		rep(i,U,c)rep(j,L,i){
    			++S[col[j]]; U[D[j]] = j; D[U[j]] = j;
    		}
    		L[R[c]] = c;
    		R[L[c]] = c;
    	}
    	int res;
    
    	bool dfs(int d){
    		if(R[0] == 0){
                ansd = d;
    			getans();
    			return true;
    		}
    		int c = R[0];
    		rep(i,R,0)if(S[i] < S[c]) c = i;
    		remove(c);
    		rep(i,D,c){
    			ans[d] = row[i];
    			rep(j,R,i) remove(col[j]);
    			//if(dfs(d+1)) return true;
    			dfs(d+1);
    			rep(j,L,i) restore(col[j]);
    		}
    		restore(c);
    		return false;
    	}
    
    	bool solve(vector<int>& v){
    		v.clear();
    		if(!dfs(0)) return false;
    		for(int i = 0; i < ansd; ++i){
    			v.push_back(ans[i]);
    		}
    		return true;
    	}
        void getans()
        {
            int tmp = 0;
            for(int i = 0; i < ansd; ++i){
                int r,c,v;
                decode(ans[i], r,c,v);
                tmp += (v+1)*sc[r][c];
            }
            res = max(res,tmp);
        }
    }dlx;
    
    
    
    void read(){
    	char s[11];
    	for(int i = 0; i < 9; ++i){
    		for(int j = 0; j < 9; ++j){
                int x;
                scanf("%d",&x);
    			puzzle[i][j] = x;
    		}
    	}
    }
    
    
    int main()
    {
        dlx.pre();  //初始化得分矩阵
        dlx.res = -1;
    	read();
    	dlx.init(324);
    	for(int r = 0; r < 9; ++r)
    		for(int c = 0; c < 9;  ++c)
    			for(int v = 0; v < 9; ++v)
    				if(puzzle[r][c] == 0 || puzzle[r][c] == v+1){
    					vector<int> cols;
    					cols.push_back(encode(SLOT, r, c));
    					cols.push_back(encode(ROW, r, v));
    					cols.push_back(encode(COL, c, v));
    					cols.push_back(encode(SUB, (r/3)*3+c/3, v) );
    					dlx.addRow(encode(r,c,v), cols);
    					if(puzzle[r][c])break;
    				}
    	//vector<int> ans;
        //dlx.solve(ans);
        dlx.dfs(0);
        printf("%d
    ",dlx.res);
        return 0;
    }
    



    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    win10怎么还原系统【系统天地】
    win7电脑玩游戏不能全屏怎么办【系统天地】
    win10显卡驱动报错怎么办【系统天地】
    win7防火墙在哪里设置【系统天地】
    怎么开启Win10远程桌面连接【系统天地】
    Win7系统C盘空间太小怎么扩容【系统天地】
    vscode 运行创建Vue项目指令出现“因为在此系统上禁止运行脚本“报错解决办法
    关于性能测试的基础——Jmeter
    学习笔记-202008
    EEPROM和flash的区别
  • 原文地址:https://www.cnblogs.com/DSChan/p/4862011.html
Copyright © 2011-2022 走看看