深搜——最优方案

Wikioi 1174 靶形数独

题目描述 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。

思路:
(摘自sth课件)

我们看每个空白的格子,看看它还可以填几个数。 如果是我们在做数独的话,我们显然会先从可以填的数少的格子开始试。 所以我们可以找出每个格子可以填几个数,然后排序,先从可以填的数少的格子开始搜。

可行性剪枝:显然,这道题的不合法解就是同一行或同一列或同一个九宫格出现了相同数字,所以我们只需要知道一个格子可以填哪些数,然后只搜索这些可以填的数就可以了

最优性剪枝: 如果当前已有得分+未来可能得到的最大的分<=当前已得到的最大总分(已经找到的合法解中的最优解),则直接退出。显然未来得分不可能超过(没有填的格子数*90)。

我们再想一下,爆搜的大部分时间都浪费在了哪? 在找这个格子能够填哪些数。 这个问题能够快速解决吗? 搜索问题中,解决这种问题有一个通用方法,就是:位运算!

既然每行每列每个九宫格一共就9个数,我们就记录一下哪些数没用过。 一共9个数,想到了啥? 2^9 压位! 若第i位的二进制为1,则表示第i个数在这一行/这一列/这个九宫格还没有出现过,还可以使用。

那我们怎么找一个格子所在的该行该列该九宫格都还未出现过的数字? 三个信息and一下就可以了。 假设得到的是x,那么,x的二进制上为1的就是我们可以在这个格子里填的数字。

怎么遍历可以填的所有数字?也就是x的所有二进制位1的位?显然不能一位一位的遍历。。。。那样就成了暴力了。。。。我们希望只遍历二进制是1的位。 For (;x!=0;x-=x&-x) { y=log[x&-x]+1;//+1是因为数字是1~9,不是0~8 ……. ……. }

注意,在给这个格子选择某个数填上,dfs进入下一层之前,我们要先修改该行该列该九宫格还能填的剩余数字。 Dfs回到上一层之后,也记得修改该行该列该九宫格还能填的剩余数字。 For (;x!=0;x-=x&-x) { y=log[x&-x]+1; 记录这个格子填y,将该格子得分加入当前总分,并将该行该列该九宫格记录的还能填的数字信息and (2^10-1-(x&-x)) dfs(………); 将该格子得分从当前总分中减去,并将该行该列该九宫格记录的还能填的数字信息or (x&-x) }

代码:

①自己写的,用了map

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<string>
  4 #include<cstring>
  5 #include<cmath> 
  6 #include<algorithm>
  7 #include<vector>
  8 #define mx 111
  9 #define maxint 10000
 10 using namespace std;
 11 struct Node{
 12     int x;
 13     int y;
 14     int a;
 15 };
 16 vector<Node> node;
 17 int a[mx][mx],line[mx],col[mx],pal[mx],block[mx][mx],ok[mx][mx],sub[mx],Log[maxint],lft,maxans,ansleft;
 18 int s_jud[9][9] = {6,6,6,6,6,6,6,6,6,
 19                    6,7,7,7,7,7,7,7,6,
 20                    6,7,8,8,8,8,8,7,6,
 21                    6,7,8,9,9,9,8,7,6,
 22                    6,7,8,9,10,9,8,7,6,
 23                    6,7,8,9,9,9,8,7,6,
 24                    6,7,8,8,8,8,8,7,6,
 25                    6,7,7,7,7,7,7,7,6,
 26                    6,6,6,6,6,6,6,6,6,};
 27 int g_jud[9][9] = {1,1,1,2,2,2,3,3,3,
 28                    1,1,1,2,2,2,3,3,3,
 29                    1,1,1,2,2,2,3,3,3,
 30                    4,4,4,5,5,5,6,6,6,
 31                    4,4,4,5,5,5,6,6,6,
 32                    4,4,4,5,5,5,6,6,6,
 33                    7,7,7,8,8,8,9,9,9,
 34                    7,7,7,8,8,8,9,9,9,
 35                    7,7,7,8,8,8,9,9,9,};
 36 bool cmp(Node x,Node y){
 37     return x.a < y.a;
 38 }
 39 void init(){
 40     lft = maxans = ansleft = 0;
 41     int vd = pow(2,9) - 1,acc;
 42     memset(line,vd,sizeof(line));
 43     memset(col,vd,sizeof(col));
 44     memset(block,vd,sizeof(block));
 45     memset(pal,vd,sizeof(pal));
 46     memset(ok,0,sizeof(ok));
 47     for(int c = 1,x = 0;c < maxint;c *= 2,x++) Log[c] = x;
 48     for(int i = 1;i <= 9;i++) sub[i] = vd - pow(2,i-1);
 49     for(int i = 1; i <= 9;i++){
 50         for(int j = 1;j <= 9;j++){
 51             cin>>a[i][j];
 52             if(a[i][j]){
 53                 lft++;
 54                 line[i] = line[i] & sub[a[i][j]];
 55                 col[j] = col[j] & sub[a[i][j]];
 56                 pal[g_jud[i-1][j-1]] = pal[g_jud[i-1][j-1]] & sub[a[i][j]];
 57                 ok[i][j] = 1;
 58                 ansleft += a[i][j] * s_jud[i-1][j-1];
 59              }
 60         }
 61     }
 62     lft = 81 - lft;
 63     Node temp;
 64     for(int i = 1;i <= 9;i++){
 65         for(int j = 1;j <= 9;j++){
 66             block[i][j] = line[i] & col[j] & pal[g_jud[i-1][j-1]];
 67             acc = 0;
 68             for(int x = block[i][j];x!=0;x-=x&-x) acc++;
 69             if(ok[i][j]) continue;
 70             temp.y = i;
 71             temp.x = j;
 72             temp.a = acc;
 73             node.push_back(temp);
 74             
 75         }
 76     }
 77     sort(node.begin(),node.end(),cmp);
 78 }
 79 void opt_init(){
 80     for(int i = 0;i <lft;i++){
 81         cout<<"No."<<i<<" ("<<node[i].x<<","<<node[i].y<<") "<<node[i].a<<endl;
 82     }
 83 }
 84 int dfs(int deep,int score){
 85     if(deep > lft){
 86         maxans = max(score,maxans);
 87         return score;
 88     }
 89     
 90     int nowx = node[deep-1].x,nowy = node[deep-1].y,test;
 91     int x = line[nowy] & col[nowx] & pal[g_jud[nowy-1][nowx-1]],y;
 92     for(;x!=0;x-=x&-x){
 93         y=Log[x&-x]+1;
 94         score += y * s_jud[nowy-1][nowx-1];
 95         test = pow(2,10) - 1 - (x&-x);
 96         line[nowy] = line[nowy] & test;
 97         col[nowx] = col[nowx] & test;
 98         pal[g_jud[nowy-1][nowx-1]] = pal[g_jud[nowy-1][nowx-1]] & test;
 99         dfs(deep+1,score);
100         score -= y * s_jud[nowy-1][nowx-1];
101         line[nowy] = line[nowy] | (x&-x);
102         col[nowx] = col[nowx] | (x&-x);
103         pal[g_jud[nowy-1][nowx-1]] = pal[g_jud[nowy-1][nowx-1]] | (x&-x);
104       }
105 }
106 int main(){
107     init();
108     dfs(1,0);
109     if(maxans) cout<<maxans + ansleft<<endl;
110     else cout<<-1<<endl;
111     return 0;
112 }
View Code

②ida*

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <cstdlib>
  5 #define MAX(a,b) a>b?a:b
  6 using namespace std;
  7 
  8 int Map[10][10]   = {0};   //这个就不用解释了吧
  9 int Nine[10][10]  = {0};    //小九宫格里每个数的使用标记,小九宫格的编号左到右,上到下,1,2,3,4,5,6,7,8,9
 10 int Line[10][10]  = {0};    //行上每个数的使用标记
 11 int Row[10][10]   = {0};   //列上每个数的使用标记
 12 int FQueL[10]     = {0};    //被启发后的行访问顺序
 13 int FQueR[10]     = {0};    //被启发后的列访问顺序
 14 int Value[10][10] = {0};    //地图各点的价值因子
 15 int QueL[81]      = {0};     //
 16 int QueR[81]      = {0};     //与上面那个,是根据启发后制定的搜索顺序的行列参数
 17 int QLen  = 0;                  //搜索队列的长度
 18 int Ans   = -1;                 //最优解
 19 int Count = 0;                  //废物变量,纪念原来我错误的写法,懒得删掉了
 20 
 21 int Swap(int *a,int *b)
 22 {
 23   int o=*a;
 24   *a=*b;
 25   *b=o;
 26 }
 27 
 28 void Heu()  //启发函数
 29 {
 30   for(int i=1;i<=9;++i){
 31     FQueL[i] = i;
 32     FQueR[i] = i;
 33     }
 34   
 35   for(int i=1;i<=8;++i)
 36     for(int j=9;j>=i+1;--j){
 37       if(Line[FQueL[j]][0] > Line[FQueL[j-1]][0])
 38           Swap(&FQueL[j],&FQueL[j-1]);
 39       if(Row[FQueR[j]][0] > Row[FQueR[j-1]][0])
 40           Swap(&FQueR[j],&FQueR[j-1]);
 41     } 
 42   for(int i=1;i<=9;++i)
 43     for(int j=1;j<=9;++j)
 44       if(Map[FQueL[i]][FQueR[j]] == 0){
 45           QueL[QLen]=FQueL[i];
 46           QueR[QLen]=FQueR[j];
 47           QLen++;
 48       }  
 49 // for(int i=1;i<=9;++i) 
 50 //      cout<<FQueL[i]<<' '<<Line[FQueL[i]][0]<<' '<<FQueR[i]<<' '<<Row[FQueR[i]][0]<<endl;
 51 } 
 52 
 53 int belong(int i,int j)     //判断行列参数为i,j的点属于哪一个九宫格。
 54 {
 55    int xt = 0,yt = 0;
 56    for(int k=6;k>=0;k-=3){
 57     if(i-k>0){xt=(k+3)/3;break;}
 58    }
 59    for(int k=6;k>=0;k-=3){
 60     if(j-k>0){yt=(k+3)/3;break;}
 61    }
 62 //   cout<<xt<<' '<<yt<<' '<<xt+(yt-1)*3<<endl;
 63 //   cout<<i<<' '<<j<<' '<< yt+(xt-1)*3 <<endl;
 64    return yt+(xt-1)*3;
 65 }
 66 
 67 int Score()           //成绩计算
 68 {
 69   int Temp = 0;
 70   for(int i=1;i<=9;++i)
 71     for(int j=1;j<=9;++j)
 72       Temp += Map[i][j]*Value[i][j];
 73   Ans = MAX(Temp,Ans);
 74 //  cout<<Ans<<endl;
 75 }
 76 
 77 int Dfs(int step)      //深度优先搜索主体
 78 {
 79 //  cout<<step<<' '<<81-Count<<endl;
 80 //  for(int i=1;i<=9;++i,cout<<endl)
 81 //    for(int j=1;j<=9;++j)
 82 //      cout<<Map[i][j]<<' ';
 83   if(step == QLen){Score();return 0;}
 84        if(!Map[QueL[step]][QueR[step]])
 85        for(int k=1;k<=9;++k){
 86              if(!Nine[belong(QueL[step],QueR[step])][k])
 87                if(!Line[QueL[step]][k] && !Row[QueR[step]][k]){
 88                 //Heu();
 89 //                cout<<FQueL[i]<<' '<<FQueR[j]<<' '<<k<<endl;
 90                    Map[QueL[step]][QueR[step]] = k;
 91                    Nine[belong(QueL[step],QueR[step])][k] = 1;
 92                    Line[QueL[step]][k]=Row[QueR[step]][k] = 1;
 93                    Line[QueL[step]][0]++;Row[QueR[step]][0]++;
 94                 Dfs(step+1); 
 95                 Map[QueL[step]][QueR[step]] = 0;
 96                    Nine[belong(QueL[step],QueR[step])][k] = 0;
 97                    Line[QueL[step]][k]=Row[QueR[step]][k] = 0;
 98                    Line[QueL[step]][0]--;Row[QueR[step]][0]--;            
 99              }                 
100          }
101    return 0;
102 }
103 
104 int main()
105 {
106   for(int i=1;i<=5;++i)
107     for(int j=1+(i-1);j<=9-(i-1);++j){
108        Value[i][j] = 6+(i-1);
109        Value[9-(i-1)][j] = 6+(i-1);
110        Value[j][9-(i-1)] = 6+(i-1);
111        Value[j][i] = 6+(i-1);
112     }   //init value    对价值表的初始,好像其他人都是直接用{}初始的.......
113 //  for(int i=1;i<=9;++i,cout<<endl)
114 //    for(int j=1;j<=9;++j)
115 //       cout<<Value[i][j]<<' ';
116   for(int i=1;i<=9;++i)
117     for(int j=1,x,y;j<=9;++j){
118        scanf("%d",&Map[i][j]);
119        if(Map[i][j] != 0 ){
120             Line[i][Map[i][j]] = 1;
121             Line[i][0]++;
122             Row[j][Map[i][j]]  = 1;
123             Row[j][0]++;
124             Nine[belong(i,j)][Map[i][j]] = 1;
125             Count++;
126        }    
127      }
128 
129 //  for(int i=1;i<=9;++i,cout<<endl)
130 //   for(int j=1;j<=9;++j)
131 //      cout<<Nine[i][j];
132 
133   Heu();
134   Dfs(0);
135   
136   cout<<Ans;
137   return 0;
138 }
View Code

Wikioi 1005 生日礼物

题目描述 Description

       9月12日是小松的朋友小寒的生日。小松知道小寒特别喜欢蝴蝶,所以决定折蝴蝶作为给小寒的生日礼物。他来到了PK大学最大的一家地下超市,在超市里,小松找到了n种可以用来折纸的本子。每种类型的本子里有若干不同颜色的纸若干张,当然同种类型的本子一定是完全一样的,而不同种类型的本子不一定完全不一样。他统计了一下,这里总共有n种不同类型的可以用来折纸的本子,每种本子各有bi本,所有的纸中有m种颜色是小寒所喜欢的颜色。小松希望他折的每种颜色的蝴蝶的数目是一样的。换句话说,小松必须折m*k只蝴蝶,其中k代表每种颜色蝴蝶的数目,这个数由小松自己来决定。但是小松又不能浪费纸,也就是说他买的本子中,只要是小寒喜欢的颜色的纸都要被折成蝴蝶。于是问题来了,每种类型的本子应该各买多少本,才能折出这m*k只蝴蝶呢?当然,由于小松是个很懒的人,他希望折的蝴蝶数目越少越好,只要表达了心意就可以了(也就是不能1只也不折)。而如果小松总共必须折1000只以上的蝴蝶才能满足要求,那么他就宁愿换一种礼物的方案了。

输入描述 Input Description

       输入的第一行包含2个整数n(1≤n8),m(1≤m10)。表示有n种不同类型的本子和m种小寒喜欢的颜色。接下来一个n*m的矩阵。第i行第j列的整数aij表示在第i种类型的本子中包含小寒喜欢的颜色j的纸有aij(1≤aij100)张。再接下来的一排n个整数b1bn,表示每种颜色的本子在超市中有多少本(1≤bi5)。

输出描述 Output Description

       输出包含一个整数,表示小松最少需要折的蝴蝶数目,如果该数目超过1000,则输出”alternative!”。(由于可能存在多种买本子的方案,所以这里就不要求输出具体方案了)

样例输入 Sample Input

2 3

2 1 2

4 8 4

5 5

样例输出 Sample Output

36

思路:

简单深搜,枚举本子数,达到递归边界判定,如果各种蝴蝶都相等并且每种蝴蝶的数目小于目前的方案,就记录,到最后再输出

代码:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<string>
 6 using namespace std;
 7 int n,m,a[20][20],b[20],ans[20],tot,jud[10000];
 8 int dfs(int deep,int add){
 9     if(add >= tot) return false;
10     if(deep == n){
11         int cmp = 0;
12         if(add == 0) return false;
13         if(jud[add]) return false;
14         for(int i = 2;i <= m;i++){
15             for(int j = 1;j <= n;j++){
16                 cmp += a[j][i] * ans[j];
17             }
18             if(cmp!=add) return false;
19             cmp = 0;
20         }
21         tot = min(tot,add);
22         jud[add] = 1;
23         return true;
24     }
25     for(int i = 0;i <= b[deep+1];i++){
26         ans[deep+1] = i;
27         dfs(deep+1,add + ans[deep+1] * a[deep+1][1]);
28     }
29     return false;
30 }
31 int main(){
32     tot = 10000000;
33     cin>>n>>m;
34     for(int i = 1;i <= n;i++){
35         for(int j = 1;j <= m;j++){
36             cin>>a[i][j];
37         }
38     }
39     for(int i = 1;i <= n;i++) cin>>b[i];
40     dfs(0,0);
41     if(tot*m <= 1000) cout<<tot*m;
42     else cout<<"alternative!";
43     return 0;
44 }
View Code

Tyvj 1266 费解的开关 

描述

    你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。
    我们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态

10111
01101
10111
10000
11011

    在改变了最左上角的灯的状态后将变成:

01111
11101
10111
10000
11011

    再改变它正中间的灯后状态将变成:

01111
11001
11001
10100
11011

    给定一些游戏的初始状态,编写程序判断游戏者是否可能在6步以内使所有的灯都变亮。

输入格式

    第一行有一个正整数n,代表数据中共有n个待解决的游戏初始状态。
    以下若干行数据分为n组,每组数据有5行,每行5个字符。每组数据描述了一个游戏的初始状态。各组数据间用一个空行分隔。
    对于30%的数据,n<=5;
    对于100%的数据,n<=500。

输出格式

    输出数据一共有n行,每行有一个小于等于6的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。
    对于某一个游戏初始状态,若6步以内无法使所有灯变亮,请输出“-1”。

测试样例1

输入


00111 
01011 
10001 
11010 
11100 

11101 
11101 
11110 
11111 
11111 

01111 
11111 
11111 
11111 
11111

输出



-1
思路:
枚举第一行的按灯方案,剩下的行需要刚好灭掉上一行没有灭掉的灯,最后再判断最后一行是否灯全部灭完
代码:
 1 #include<cstring> 
 2 #include<cstdlib> 
 3 #include<cstdio> 
 4 #include<iostream> 
 5 using namespace std; 
 6 
 7 typedef int array[6]; 
 8 array a,b[6],c[6]; 
 9 int n,ans; 
10 void Init() 
11 { 
12 char str[6]; 
13 for (int i=1;i<=5;i++) 
14 { 
15 scanf("%s",str); 
16 for (int j=1;j<=5;j++) 
17 b[i][j]=str[j-1]-'0'; 
18 } 
19 } 
20 int work() 
21 { 
22 int dep=0; 
23 for (int i=1;i<=5;i++) 
24 if (a[i]==1) 
25 { 
26 dep++; 
27 c[1][i]^=1; c[2][i]^=1; 
28 if (i>1) c[1][i-1]^=1; 
29 if (i<5) c[1][i+1]^=1; 
30 } 
31 for (int i=2;i<=5;i++) 
32 for (int j=1;j<=5;j++) 
33 if (c[i-1][j]==0 && dep<=6) 
34 { 
35 dep++; 
36 c[i][j]^=1; c[i-1][j]^=1; 
37 if (j>1) c[i][j-1]^=1; 
38 if (j<5) c[i][j+1]^=1; 
39 if (i<5) c[i+1][j]^=1; 
40 } 
41 if (dep>=7) return 7; 
42 for (int i=1;i<=5;i++) 
43 if (c[5][i]==0) return 7; 
44 return dep; 
45 } 
46 void solve() 
47 { 
48 Init(); 
49 ans=0xFFFFFFF; 
50 for (int a1=0;a1<=1;a1++) 
51 for (int a2=0;a2<=1;a2++) 
52 for (int a3=0;a3<=1;a3++) 
53 for (int a4=0;a4<=1;a4++) 
54 for (int a5=0;a5<=1;a5++) 
55 { 
56 memcpy(c,b,sizeof(c)); 
57 a[1]=a1; a[2]=a2; a[3]=a3; a[4]=a4; a[5]=a5; 
58 int step=work(); 
59 if (step<ans) ans=step; 
60 if (ans==2) 
61 { 
62 printf("2
"); 
63 return ; 
64 } 
65 } 
66 if (ans<=6) printf("%d
",ans); 
67 else printf("-1
"); 
68 } 
69 int main() 
70 { 
71 scanf("%d",&n); 
72 for (int test=1;test<=n;test++) solve(); 
73 return 0; 
74 }
View Code
 Wikioi 2495 水叮当的舞步
题目描述 Description

  水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变。
  为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~

  地毯上的格子有N行N列,每个格子用一个0~5之间的数字代表它的颜色。
  水叮当可以随意选择一个0~5之间的颜色,然后轻轻地跳动一步,左上角的格子所在的联通块里的所有格子就会变成她选择的那种颜色。这里连通定义为:两个格子有公共边,并且颜色相同。
  由于水叮当是施展轻功来跳舞的,为了不消耗过多的真气,她想知道最少要多少步才能把所有格子的颜色变成一样的。

输入描述 Input Description

  每个测试点包含多组数据。
  每组数据的第一行是一个整数N,表示地摊上的格子有N行N列。
  接下来一个N*N的矩阵,矩阵中的每个数都在0~5之间,描述了每个格子的颜色。
  N=0代表输入的结束。

输出描述 Output Description

  对于每组数据,输出一个整数,表示最少步数。

样例输入 Sample Input

2
0 0 
0 0
3
0 1 2
1 1 2
2 2 1
0

样例输出 Sample Output

0
3

数据范围及提示 Data Size & Hint

  对于30%的数据,N<=5
  对于50%的数据,N<=6
  对于70%的数据,N<=7
  对于100%的数据,N<=8,每个测试点不多于20组数据。

第二组样例解释:
  0 1 2       1 1 2       2 2 2      1 1 1
  1 1 2 --> 1 1 2 --> 2 2 2 --> 1 1 1
  2 2 1       2 2 1       2 2 1      1 1 1

来源:Nescafe 21

思路:

IDA*

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<iostream>
 5 #include<cstdlib>
 6 #include<algorithm>
 7 using namespace std;
 8 const int dx[4]={0,0,1,-1};//扩展联通块时会用上。
 9 const int dy[4]={-1,1,0,0};//扩展联通块时会用上。
10 int n,ID;//ID用来标记搜索深度。
11 int map[10][10],cl[10][10];//cl用来存储联通块。
12 bool used[6];//在估价函数中用来判重。
13  
14 int get()//获得估价值。
15 {
16        int ret=0;
17        memset(used,false,sizeof(used));
18        for(int i=1;i<=n;i++)
19        {
20               for(int j=1;j<=n;j++)
21               {
22                      if(!used[map[i][j]] && cl[i][j]!=1)
23                      {
24                             used[map[i][j]]=true;
25                             ret++;
26                      }
27               }
28        }
29        return ret;
30 }
31  
32 void dfs(int x,int y,int color)//对联通块进行扩展,color标记当前染的颜色。
33 {
34        cl[x][y]=1;
35        for(int i=0;i<4;i++)
36        {
37               int nx=x+dx[i],ny=y+dy[i];
38               if(nx<1 || ny<1 || nx>n || ny>n || cl[nx][ny]==1)continue;//已经在联通块中了。
39               cl[nx][ny]=2;
40               if(map[nx][ny]==color)dfs(nx,ny,color);//如果发现该点颜色与要染的颜色相同,就以它重新扩展边界。
41        }
42 }
43  
44 int fill(int color)//该次染色对联通块大小有无影响的判断函数。
45 {
46        int ret=0;
47        for(int i=1;i<=n;i++)
48        {
49               for(int j=1;j<=n;j++)
50               {
51                      if(cl[i][j]==2 && map[i][j]==color)
52                      {
53                             ret++;
54                             dfs(i,j,color);
55                      }
56               }
57        }
58        return ret;
59 }
60  
61 bool IDAstar(int step)//IDAstar的迭代搜索过程。
62 {
63        int g=get();//得到估价值。
64        if(step+g>ID)return false;//超过迭代搜索范围,返回假。
65        if(!g)return true;//如果找到答案,返回真。
66       
67        int temp[10][10];//用来暂时保存当前联通块状态。
68        for(int i=0;i<=5;i++)
69        {
70               memcpy(temp,cl,sizeof(map));//用memcpy函数快捷存储。
71               if(fill(i) && IDAstar(step+1))return true;//如果此次染色有利于扩大联通块并且染色后能得到答案,就返回真。
72               memcpy(cl,temp,sizeof(map)); //用memcpy函数快捷存储。
73        }
74        return false;//以上条件都不满足,返回假。
75 }
76 main(){
77 while(cin>>n && n)
78        {
79               memset(cl,0,sizeof(cl));
80               for(int i=1;i<=n;i++)
81               {
82                      for(int j=1;j<=n;j++)
83                      {
84                             scanf("%d",&map[i][j]);
85                      }
86               }
87               dfs(1,1,map[1][1]);
88               for(ID=0;;ID++)//枚举搜索深度限制。
89               {
90                      if(IDAstar(0))
91                      {
92                             break;//得到答案后退出循环。
93                      }
94               }
95               cout<<ID<<endl;//输出的为退出时的ID值,即为答案。
96        }
97 }
View Code
原文地址:https://www.cnblogs.com/hyfer/p/4823788.html