Kakuro - Cross Sums
问题如下 一个简单的例子
可以看出限制条件是某行或某列的某几个空白格子求和等于某个值,且每一个限制中的格子所填的数必须为1-9且互异。
直接暴力搜索,空白格子太多,复杂度是无法接受的。
我们考虑用GAC加速。
所谓GAC就是在填充一个变量的时候,保证和这个变量相关的所有限制的所有变量都是一致的!
什么是一致呢? 就是对一个变量的论域内的所有可取的值,其他相关变量可以有一种取值满足限制条件。
如果我们在搜索的全过程都能满足GAC的一致性,那么每一步所选的变量,最后就可以是一个有效的解。
下面来解释一下具体的GAC搜索过程
初始状态,所有的变量论域都是满的,我们的目的是找到一个解,也就是说目标状态是这样的:
所有变量的论域中有且只有一个可选变量,同时满足GAC。根据GAC的定义,这显然就是CSP问题的解了。
如何从初始状态达到目标状态呢? 显然就是删除变量论域中的可取值了(也就是对变量赋值)
每次选择一个变量,把目标值以外的其他值全部从论域中删除,然后进行GAC ,达到一致状态,然后继续选取变量,继续GAC...
有一点要注意,搜索是有回溯的,所以所有删除的论域取值都要进行标记,以便restore。
下面给出伪代码
对于15*15的数据,我的cpp代码运行效果如下
下面给出cpp代码 和6个测试数据
// Wang Bingsen 15336169 #include<bits/stdc++.h> using namespace std; struct Variable { bool Domain[10]; int Row,Col,dsize; int Value; Variable() { this->Value=0; this->dsize=9; memset(Domain,true,sizeof(Domain)); Row=Col=-1; } }; struct Constrain { bool Used[10]; int Goal; vector<int>Scop; Constrain() { memset(Used,false,sizeof(Used)); Goal=-1; Scop.resize(0); } Constrain(int c) { Scop.resize(0); memset(Used,false,sizeof(Used)); Goal=0; this->Goal=c; } }; inline char getc(); void Init(); bool Support(int seted,Constrain & C); bool GAC_Enforce(queue<int> & P,vector<pair<int,int> > & Restore); void Reimburse(vector<pair<int,int> >& Re); bool Set(); bool Solve(); vector<Variable> Var; vector<Constrain> Con; int n,m; typedef pair<int,int> Problem; Problem Prob[20][20]; inline char getc() { char c; while(true) { scanf("%c",&c); if(c!=' '&&c!='\n') return c; } return c; } void Init() { Var.clear(); Var.resize(0); Con.clear(); Con.resize(0); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { char ch=getc(); if(ch=='*') { Prob[i][j]=make_pair(-1,-1); continue; } if(ch=='0') { Variable nV; Prob[i][j]=make_pair(-3,(int)Var.size()); Var.push_back(nV); continue; } if(ch=='(') { int a=0,b=0; scanf("%d%c%d%c",&a,&ch,&b,&ch); int c=-2,d=-2; if(a!=0) { Constrain nC(a); c=(int)Con.size(); Con.push_back(nC); } if(b!=0) { Constrain nC(b); d=(int)Con.size(); Con.push_back(nC); } Prob[i][j]=make_pair(c,d); continue; } } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(Prob[i][j].first==-1||Prob[i][j].first==-3) continue; int a=Prob[i][j].first,b=Prob[i][j].second; if(a!=-2) { int ii=i+1; vector<int> vec; while(ii<=n&&Prob[ii][j].first==-3) { vec.push_back(Prob[ii][j].second); ii++; } for(int k=0;k<vec.size();k++) { Var[vec[k]].Col=a; Con[a].Scop.push_back(vec[k]); } } if(b!=-2) { int jj=j+1; vector<int> vec; while(jj<=m&&Prob[i][jj].first==-3) { vec.push_back(Prob[i][jj].second); jj++; } for(int k=0;k<vec.size();k++) { Var[vec[k]].Row=b; Con[b].Scop.push_back(vec[k]); } } } } } bool Support(int seted,Constrain & C) { if(C.Goal<=0)return false; bool ret=false; int loc=-1,min=11; for(int i=0;i<C.Scop.size()&&min>2;i++) { int nv=C.Scop[i]; if(Var[nv].Value!=0)continue; if(loc==-1||Var[nv].dsize<min){loc=nv;min=Var[nv].dsize;break;} } if(loc==-1)return false; if(seted==(int)C.Scop.size()) { if(C.Goal<=9&&(!C.Used[C.Goal])&&Var[loc].Domain[C.Goal])return true; else return false; } for(int v=9;v>=1&&!ret;v--) { if(!Var[loc].Domain[v]||v>=C.Goal||C.Used[v])continue; Var[loc].Value=v; C.Goal-=v; C.Used[v]=true; ret=Support(seted+1,C); Var[loc].Value=0; C.Goal+=v; C.Used[v]=false; } return ret; } bool cmp(int a,int b) { return Var[a].dsize<Var[b].dsize; } bool GAC_Enforce(queue<int> & P,vector<pair<int,int> > & Restore) /* This implementation has refered the pseudo_code for GAC in the Course Slide week8.pdf. Other than that, there should be no such thing like plagiarize. ╮(╯_╰)╭ */ { bool inq[1000]; memset(inq,false,sizeof(inq)); queue<int> Q; while(!P.empty()) { int Fr=P.front();P.pop(); inq[Fr]=true; Q.push(Fr); } while(!Q.empty()) { int Fr=Q.front();Q.pop(); inq[Fr]=false; sort(&Con[Fr].Scop[0],&Con[Fr].Scop[0]+(int)Con[Fr].Scop.size(),cmp);//MRV for(int i=0;i<Con[Fr].Scop.size();i++) { int nv=Con[Fr].Scop[i]; if(Var[nv].dsize<=0)return false; for(int v=9;v>=1;v--) { if(!Var[nv].Domain[v]||Con[Fr].Used[v])continue; Var[nv].Value=v; Con[Fr].Goal-=v; Con[Fr].Used[v]=true; if(!Support(2,Con[Fr])) { Var[nv].Domain[v]=false; Var[nv].dsize--; Restore.push_back(make_pair(nv,v)); if(Var[nv].dsize==0) { while(!Q.empty())Q.pop(); Var[nv].Value=0; Con[Fr].Goal+=v; Con[Fr].Used[v]=false; return false; } else { int a=Var[nv].Col,b=Var[nv].Row; if(a!=-1&&(!inq[a])){inq[a]=true;Q.push(a);} if(b!=-1&&(!inq[b])){inq[b]=true;Q.push(b);} } } Var[nv].Value=0; Con[Fr].Goal+=v; Con[Fr].Used[v]=false; } } } return true; } void Reimburse(vector<pair<int,int> >& Re) { for(int i=0;i<Re.size();i++) { Var[Re[i].first].Domain[Re[i].second]=true; Var[Re[i].first].dsize++; } return ; } bool Set() { bool ret=false; int loc=-1,min=11; for(int i=0;i<Var.size()&&min>2;i++) { if(Var[i].dsize==1)continue; if(Var[i].dsize==0)return false; if(loc==-1||Var[i].dsize<min){loc=i;min=Var[i].dsize;} } if(loc==-1)return true; for(int v=9;v>=1&&!ret;v--) { if(!Var[loc].Domain[v])continue; vector<pair<int,int> > Re;Re.resize(0); for(int vv=9;vv>=1;vv--) { if(vv==v||(!Var[loc].Domain[vv]))continue; Var[loc].Domain[vv]=false; Var[loc].dsize--; Re.push_back(make_pair(loc,vv)); } queue<int> Q; while(!Q.empty())Q.pop(); int a=Var[loc].Col,b=Var[loc].Row; if(a!=-1)Q.push(a);if(b!=-1)Q.push(b); if(GAC_Enforce(Q,Re)) { ret=Set(); if(ret)return true; } Reimburse(Re); } return ret; } bool Solve() { /*queue<int>Q; for(int i=0;i<Con.size();i++) Q.push(i); vector<pair<int,int> > V; GAC_Enforce(Q,V); initiate GAC not necessary*/ if(!Set())return false; for(int i=0;i<Var.size();i++) { for(int v=9;v>=1;v--) { if(Var[i].Domain[v]){Var[i].Value=v;break;} } } return true; } void spa(int x) { for(int i=0;i<x;i++) printf(" "); } void Print(bool flg) { if(!flg)printf("%dX%d\nInput:\n",n,m); else printf("\n\nSolution:\n"); int ns=6; for(int i=1;i<=n;i++) { ns=6; for(int j=1;j<=m;j++) { if(Prob[i][j].first==-3) { spa(ns);printf("%d",Var[Prob[i][j].second].Value); ns=6; continue; } if(Prob[i][j].first==-1) { spa(ns);printf("*"); ns=6; continue; } int nr=6,a=0,b=0; if(Prob[i][j].first!=-2&&(a=Con[Prob[i][j].first].Goal)>=10)ns-=3; else ns-=2; if(Prob[i][j].second!=-2&&(b=Con[Prob[i][j].second].Goal)>=10)nr-=3; else nr-=2; spa(ns);printf("(%d,%d)",a,b); ns=nr; } printf("\n"); } } int main() { for(int i=0;i<=5;i++) { char a[]="test5.txt"; char b[]="text5ans.txt"; a[4]=i+'0'; b[4]=a[4]; // printf("\n\n%s",b); freopen(a,"r",stdin); freopen(b,"w",stdout); Init(); Print(0); if(Solve()){printf("Exists one solution\n");Print(1);} else printf("No solution found\n"); } return 0; } /* test0.txt 9 8 * * *(20,0) (7,0) * * * * *(21,4) 0 0 (8,0) * * * (0,21)0 0 0 0(27,0) * *(12,11)0 0 (0,15)0 0 (16,0) (0,16)0 0 * * (0,8) 0 0 (0,8) 0 0(15,0) *(20,16)0 0 * (0,11)0 0 (9,8) 0 0 * * * (0,20)0 0 0 0 * * * * (0,17) 0 0 * * test1.txt 4 4 * (23,0)(21,0) (7,0) (0,20) 0 0 0 (0,19) 0 0 0 (0,12) 0 0 0 test2.txt 5 5 *(16,0) (7,0) * * (0,9) 0 0(24,0) * (0,20)0 0 0 (4,0) * (0,12) 0 0 0 * * (0,10)0 0 test3.txt 9 8 * (16,0) (6,0) * * (8,0)(29,0) * (0,13) 0 0 (15,0)(16,13) 0 0 * (0,28) 0 0 0 0 0 0 (11,0) * * (30,15) 0 0 (0,3) 0 0 * (16,8) 0 0 * (22,14) 0 0 (0,14) 0 0 * (9,17) 0 0 * (0,13) 0 0 (5,10) 0 0 (12,0) (8,0) * (0,32) 0 0 0 0 0 0 * (0,11) 0 0 * (0,12) 0 0 test4.txt 13 13 * (16,0) (7,0) * (16,0) (17,0) * * (10,0) (16,0) * * * (0,9) 0 0 (0,10) 0 0 (3,0) (0,9) 0 0 * * * (0,10) 0 0 (16,12) 0 0 0 (3,10) 0 0 * * * * (0,17) 0 0 0 (0,6) 0 0 0 (16,0) * * * * * (0,12) 0 0 (17,0) (6,14) 0 0 0 (6,0) * * * * (3,0) (6,13) 0 0 0 (7,0) (0,10) 0 0 (16,0) * * (0,4) 0 0 (0,14) 0 0 0 (16,0) (0,10) 0 0 * * (0,3) 0 0 (16,0) (0,12) 0 0 0 (35,9) 0 0 * * * (0,9) 0 0 (10,0) (17,19) 0 0 0 (16,0) * * * * * (0,21) 0 0 0 (16,0) (0,14) 0 0 (24,0) * * * * * (16,17) 0 0 0 (4,24) 0 0 0 (3,0) * * * (0,10) 0 0 (0,19) 0 0 0 (0,11) 0 0 * * * (0,11) 0 0 * (0,7) 0 0 (0,8) 0 0 test5.txt 15 15 * * * (35,0) (17,0) (6,0) * (17,0) (6,0) * (16,0) (6,0) * * * * * (0,15) 0 0 0 (0,12) 0 0 (28,9) 0 0 * * * * * (4,18) 0 0 0 (16,26) 0 0 0 0 0 (4,0) * * * (0,9) 0 0 (0,10) 0 0 (4,4) 0 0 (3,4) 0 0 (39,0) * * (0,12) 0 0 (16,0) (0,12) 0 0 (17,5) 0 0 (0,9) 0 0 (3,0) * (4,0) (22,12) 0 0 (24,0) (0,19) 0 0 0 0 * (16,5) 0 0 (0,10) 0 0 (0,17) 0 0 (41,0) (0,10) 0 0 (23,0) (0,18) 0 0 0 (0,4) 0 0 (17,0) (0,9) 0 0 * (0,7) 0 0 (0,14) 0 0 (16,0) * (4,13) 0 0 (0,17) 0 0 (4,0) (0,14) 0 0 (3,0) (0,16) 0 0 (0,13) 0 0 0 * (17,5) 0 0 (16,0) (0,9) 0 0 (16,15) 0 0 (0,3) 0 0 (16,0) (0,25) 0 0 0 0 (3,0) (0,6) 0 0 (16,0) * * (0,11) 0 0 (24,15) 0 0 (7,11) 0 0 (6,0) (0,10) 0 0 * * * (0,16) 0 0 (4,9) 0 0 (16,3) 0 0 (16,11) 0 0 * * * * (0,27) 0 0 0 0 0 (0,13) 0 0 0 * * * * * (0,12) 0 0 (0,10) 0 0 (0,14) 0 0 0 * * */