HDU - 3909

HDU - 3909
Time Limit: 20000MS Memory Limit: 65536KB 64bit IO Format: %I64d & %I64u

 Status

Description

AmazingCaddy likes Sudoku very much. One day, he got some Sudoku problems and he wondered whether these problems are well designed. He needs your help now. 

Sudoku is a logic-based, combinatorial number-placement puzzle. The objective is to fill a 9 × 9 grid with digits so that each column, each row, and each of the nine 3 × 3 regions contain all of the digits from 1 to 9. 
-- Wikipedia 


In this problem, the grid has three different sizes, with the formal (N^2) × (N^2), and N will be one in 2, 3 and 4. The rule is the same. The objective is to fill the (N^2) × (N^2) grid with characters so that each column, each row, and each of the N^2 regions (each size is N × N) contains all of the characters from 1 to 4(N = 2), 1 to 9(N = 3) or 1 to G (N = 4). 

You task is that when you got a grid, you should tell the grid whether a puzzle or not. If it`s a puzzle, you should tell whether it is a minimal puzzle; else you should tell it has no solution or has multiple solutions. 

A puzzle is a partially completed grid (the size is (N^2) × (N^2), N = 2, 3, 4), and has one and only one solution. If remove any character from a puzzle, it will lead to multiple solutions, then it is called a minimal puzzle. 

 

Input

The input contains several cases. For each case, the first line of the input is the N (2<= N <=4). The next N^2 lines will contain the grid. Each line will have N^2 characters, represent the grid. The empty cell will be represented as ’.’. All input data is legal, and you can get more information from the sample input. 
 

Output

For each case: 
If the grid is not a puzzle, and has no solution, print “No Solution”, or has multiple solutions print “Multiple Solutions”. 
If the grid is a puzzle, but not a minimal, print “Not Minimal”, or print N lines represent the answer in the same format in the input, and ‘.’ should be replaced with the right characters. You can get more information from the sample output. 

 

Sample Input

        
2 4312 12.4 .... ...1 3 5...9.31. 71.8...9. 32...6... ........3 .8.5.3... 4....1.5. 8..9...4. ...1..9.. .....7... 4 ...86.....D.A... .A.C.G.49....65E .......3B61C..DG 3...89.D7....24. ....4..G.9F2BD.. 49...C5.....7... 1.C..8.B6....... .6A.2.D...4.89.5 8F3BG.E24.A...7. ...6.3.A.1...... D.........5.1E2. .G...D.9........ ......F6.7.....3 6D.9.7..EG...B.. 51B.A..8........ ........F.C..71.
 

Sample Output

        
Not Minimal Multiple Solutions 9BG86F253ED4A1C7 7ADC1GB4928F365E F245EA73B61C98DG 3E6189CD7AG5F24B E873461G59F2BDAC 492D3C5FA8EB7G61 15CF98AB6D7G43E2 B6AG2ED7C34189F5 8F3BG1E24CAD6579 C756F38A219EG4BD D49A7B6CGF531E28 2G1E5D498B67CF3A GCE4D5F617B82A93 6DF9C731EG2A5B84 51B7A298D436ECGF A382B4GEF5C9D716
 

Source

2011 Multi-University Training Contest 7 - Host by ECNU
最全面的一道数独题了吧,最高可到16阶。。
这几天在研究DLX的时候,看到一篇文章,说了未经优化的dlx和经过优化的dlx差距巨大。而每次从1最少的列开始dance的话,可以优化大量的时间。一开始我对这句话是很怀疑的,感觉先后顺序应该和时间没什么关系吧,毕竟就是乘法满足交换律。直到我今天亲自写了一下,,,
一开始写了个未经优化的DLX,用上面的样例,第一个样例秒出,而第二个样子居然用了大概42s左右的时间才出结果。。第三个样例等了20多分钟,,怕把cpu烧坏就中止了。。。随后又写了个 每次从1最少的列开始dance的程序,,所用时间看图。。
3个样例一起运行居然只用了2s左右,,和未优化的相差几千倍啊,,太诡异了。。。
下面是AC代码,用时7s,,是有点长。。然后我又在网上找了一种听说很好的方法,那就是先构造已经有数字的格子行,然后在构造不定的格子的时候,将与已有格子行冲突的全弃掉。。。。。我也满怀期待的写了一下,,结果运行时间居然和原先差不多!几乎是一模一样。。。。统计了下对于第三个样例最后构造成01矩阵的行数,原先是大概2705行,经过“优化”后,行数减到了900多行。。可以说大量减少。。。可是时间为啥一样啊。。。。。。
#include<iostream>
#include<cstdio>
#include<math.h>
#include<algorithm>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<string.h>
#include<cstring>
#include<vector>
using namespace std;
#define INF 0x3f3f3f3f
#define mem(x,y) memset(x,y,sizeof(x))
typedef long long LL;
int k,n,colnum[4*16*16+10],cnt,totnum;
char sudoku[17][17],sudoku1[17][17];
stack<int> mystack,mystack1;
struct {
    int u,d,l,r,x,y;
} node[200000];
void buildnode(int x,int y,int v) {
    colnum[y]++;
    cnt++;
    node[cnt]= {cnt,cnt,cnt,cnt,x,y};
    //与上下节点连接
    node[node[y].u].d=cnt;
    node[cnt].u=node[y].u;
    node[cnt].d=y;
    node[y].u=cnt;
    //与左右节点连接
    if(node[cnt-1].x==x) {
        node[node[cnt-1].r].l=cnt;
        node[cnt].r=node[cnt-1].r;
        node[cnt-1].r=cnt;
        node[cnt].l=cnt-1;
    }
}
void buildhang(int i,int j,int x) {
    int xx=((i-1)*n+j-1)*n+x;
    buildnode(xx,n*(k*((i-1)/k)+(j-1)/k)+x,x);//宫
    buildnode(xx,n*n+(i-1)*n+x,x);//行
    buildnode(xx,2*n*n+(j-1)*n+x,x);//列
    buildnode(xx,3*n*n+(i-1)*n+j,x);//格子
}
int getnum(char c) {
    if(c>='A') return 10+c-'A';
    else return c-'0';
}
void build() {
    //将node【0】作为整个dancelink的头结点
    node[0]= {0,0,0,0,0,0}; //初始化头结点
    int prenode=0;
    for(int i=1; i<=n*n*4; i++) { //初始化表头节点
        node[i]= {i,i,i,i,0,i};
        node[i].r=node[prenode].r;
        node[node[prenode].r].l=i;
        node[prenode].r=i;
        node[i].l=prenode;
        prenode=i;
    }
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++) {
            if(sudoku[i][j]!='.') buildhang(i,j,getnum(sudoku[i][j]));
            else for(int w=1; w<=n; w++) buildhang(i,j,w);
        }
}
void Remove(int x) {
    node[node[x].l].r=node[x].r;
    node[node[x].r].l=node[x].l;
    int nownode=node[x].d,p;
    while(nownode!=x) {
        p=node[nownode].r;
        while(p!=nownode) {
            colnum[node[p].y]--;
            node[node[p].u].d=node[p].d;
            node[node[p].d].u=node[p].u;
            p=node[p].r;
        }
        nownode=node[nownode].d;
    }
}
void Resume(int x) {
    node[node[x].l].r=x;
    node[node[x].r].l=x;
    int nownode=node[x].d,p;
    while(nownode!=x) {
        p=node[nownode].r;
        while(p!=nownode) {
            colnum[node[p].y]++;
            node[node[p].u].d=p;
            node[node[p].d].u=p;
            p=node[p].r;
        }
        nownode=node[nownode].d;
    }
}
int minnode() {//最小头结点
    int minn=INF,y=0,nownode=node[0].r;
    while(nownode!=0) {
        if(colnum[node[nownode].y]<minn) {
            minn=colnum[node[nownode].y];
            y=node[nownode].y;
        }
        nownode=node[nownode].r;
    }
    return y;
}
int dance() {
    int nownode=minnode(),p1,p2;
    if(nownode==0) {
        totnum++;
        if(totnum==2) return 1;
        mystack1=mystack;
        return 0;
    }
    p1=node[nownode].d;
    if(p1==nownode) return 0;
    Remove(node[nownode].y);
    while(p1!=nownode) {
        mystack.push(node[p1].x);
        p2=node[p1].r;
        while(p2!=p1) {
            Remove(node[p2].y);
            p2=node[p2].r;
        }
        if(dance()) return 1;
        p2=node[p1].l;
        while(p2!=p1) {
            Resume(node[p2].y);
            p2=node[p2].l;
        }
        mystack.pop();
        p1=node[p1].d;
    }
    Resume(node[nownode].y);
    return 0;
}
int _find1(int x) {
    return (x-1)/n+1;
}
char Getchar(int x) {
    int t=(x-1)%n+1;
    if(t<=9) return '0'+t;
    else return 'A'+t-10;
}
void print() {//打印路径
    for(int i =1; i<=n; i++) {
        for(int j=1; j<=n; j++)cout<<sudoku1[i][j];
        cout<<endl;
    }
}
void DLX() {
    while(!mystack.empty()) mystack.pop();
    totnum=0;
    mem(colnum,0);
    cnt=n*n*4;
    build();
    dance();
}
bool is_min() {//是否为<span style="font-family: Arial, Helvetica, sans-serif;">Minimal
</span>char c;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++) {
            if(sudoku[i][j]!='.') {
                c=sudoku[i][j];
                sudoku[i][j]='.';
                DLX();
                sudoku[i][j]=c;
                if(totnum!=2) return false;
            }
        }
    return 1;
}
int main() {
   // freopen("input.txt","r",stdin);
    while(cin>>k) {
        n=k*k;
        char c;
        for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) cin>>sudoku[i][j];
        DLX();
        if(totnum==2) cout<<"Multiple Solutions"<<endl;
        else if(totnum==0) {
            cout<<"No Solution"<<endl;
        } else {
            while(!mystack1.empty()) {
                int x=mystack1.top();
                mystack1.pop();
                int gps=_find1(x);
                sudoku1[(gps-1)/n+1][(gps-1)%n+1]=Getchar(x);
            }
            if(is_min())print();
            else cout<<"Not Minimal"<<endl;
        }
    }
    return 0;
}


(1)普通用户端(全平台) 音乐播放核心体验: 个性化首页:基于 “听歌历史 + 收藏偏好” 展示 “推荐歌单(每日 30 首)、新歌速递、相似曲风推荐”,支持按 “场景(通勤 / 学习 / 运动)” 切换推荐维度。 播放页功能:支持 “无损音质切换、倍速播放(0.5x-2.0x)、定时关闭、歌词逐句滚动”,提供 “沉浸式全屏模式”(隐藏冗余控件,突出歌词与专辑封面)。 多端同步:自动同步 “播放进度、收藏列表、歌单” 至所有登录设备(如手机暂停后,电脑端打开可继续播放)。 音乐发现与管理: 智能搜索:支持 “歌曲名 / 歌手 / 歌词片段” 搜索,提供 “模糊匹配(如输入‘晴天’联想‘周杰伦 - 晴天’)、热门搜索词推荐”,结果按 “热度 / 匹配度” 排序。 歌单管理:创建 “公开 / 私有 / 加密” 歌单,支持 “批量添加歌曲、拖拽排序、一键分享到社交平台”,系统自动生成 “歌单封面(基于歌曲风格配色)”。 音乐分类浏览:按 “曲风(流行 / 摇滚 / 古典)、语言(国语 / 英语 / 日语)、年代(80 后经典 / 2023 新歌)” 分层浏览,每个分类页展示 “TOP50 榜单”。 社交互动功能: 动态广场:查看 “关注的用户 / 音乐人发布的动态(如‘分享新歌感受’)、好友正在听的歌曲”,支持 “点赞 / 评论 / 转发”,可直接点击动态中的歌曲播放。 听歌排行:个人页展示 “本周听歌 TOP10、累计听歌时长”,平台定期生成 “全球 / 好友榜”(如 “好友中你本周听歌时长排名第 3”)。 音乐圈:加入 “特定曲风圈子(如‘古典音乐爱好者’)”,参与 “话题讨论(如‘你心中最经典的钢琴曲’)、线上歌单共创”。 (2)音乐人端(创作者中心) 作品管理: 音乐上传:支持 “无损音频(FLAC/WAV)+ 歌词文件(LRC)+ 专辑封面” 上传,填写 “歌曲信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值