首页 > 代码库 > 中山大学校队选拔赛第一章题4【简单数迷Simple Kakuro】-------2015年1月28日

中山大学校队选拔赛第一章题4【简单数迷Simple Kakuro】-------2015年1月28日

一:题意描述

本题就是给定一个迷宫,其中第一行和第一列都给定了数值。现在我们的任务就是需要把剩余的空格用1-9的数字把它填满,并且每行每列数值之和需要和行列标定的值相等。问最后是否可行,如果有多种方案需要输出一种方案。

二 :题目分析

本题主要考查DFS当中剪枝技巧的利用以及DFS的方向规划问题。

 首先我们可以根据题目已知信息得出行之和必须等于列之和。(第一次剪枝)

然后我们可以根据每一个数字不能在同一行或者同一列重复出现【设定标记】进行剪枝(第二次剪枝)

对于如果某一行(一列)数值还没有填满但是总和已经超过行列标定值之和,或者填满了但是总和和标定值不相等,那么我们必须剪枝。(第三次剪枝)

对于规划方向,我们可以采取从左到右从上到下的方式进行,当我们深搜把最后一个点深搜完毕时我们即可结束DFS,当然当sol(解决办法)>1时也可以结束递归。

三:AC代码

#include<iostream>#include<cstdio>#include<string.h>#include<math.h>#include<algorithm>using namespace std;#define maxn 300+10#define MOD 1000000000int sumrow[11],sumcol[11];//行列指定值之和int a[11][11],mark[11][11];//标记矩阵确定是否已经填上了数字int  n,m;int curSumrow[11],curSumcol[11];//已经填上的行列值和int sol;int ans[11][11];int usedrow[11][10],usedcol[11][10];void Search(int x,int y){    if(sol>1)  return;//说明有多种输出方案    if(x==n)//表明已经合乎要求    {        sol++;        for(int i=0;i<n;i++)            for(int j=0;j<m;j++)            ans[i][j]=a[i][j];        return;    }    int nx,ny;    if(y==m-1)    {        nx=x+1;        ny=0;    }    else    {        nx=x;        ny=y+1;    }    if(mark[x][y])    {        int ok=1;        if(y<m-1&&curSumrow[x]>sumrow[x]||y==m-1&&curSumrow[x]!=sumrow[x])        ok=0;        if(x<n-1&&curSumcol[y]>sumcol[y]||x==n-1&&curSumcol[y]!=sumcol[y])        ok=0;        if(ok) Search(nx,ny);        return;    }    for(int i=1;i<10;i++)    if(!usedrow[x][i]&&!usedcol[y][i])    {        usedcol[y][i]=1;        usedrow[x][i]=1;        curSumcol[y]+=i;        curSumrow[x]+=i;        a[x][y]=i;        int go=1;        if(y<m-1&&curSumrow[x]>sumrow[x]||y==m-1&&curSumrow[x]!=sumrow[x])          go=0;        if(x<n-1&&curSumcol[y]>sumcol[y]||x==n-1&&curSumcol[y]!=sumcol[y])          go=0;        if(go) Search(nx,ny);        a[x][y]=0;        usedcol[y][i]=0;        usedrow[x][i]=0;        curSumcol[y]-=i;        curSumrow[x]-=i;    }}int main(){    int T;    int cas;    int sumR,sumC;    cin>>T;    for(cas=1;cas<=T;cas++)    {        cin>>n>>m;        sumC=sumR=0;        int ok=1;        memset(mark,0,sizeof(mark));        memset(usedcol,0,sizeof(usedcol));        memset(usedrow,0,sizeof(usedrow));        memset(curSumcol,0,sizeof(curSumcol));        memset(curSumrow,0,sizeof(curSumcol));        for(int i=0;i<n;i++) {cin>>sumrow[i];sumR+=sumrow[i];}        for(int j=0;j<m;j++) {cin>>sumcol[j];sumC+=sumcol[j];}        for(int i=0;i<n;i++)        for(int j=0;j<m;j++)        {            cin>>a[i][j];            if(a[i][j]>0)            {                mark[i][j]=1;                sumcol[j]+=a[i][j];                sumrow[i]+=a[i][j];                usedrow[i][a[i][j]]++;                usedcol[j][a[i][j]]++;                if(usedrow[i][a[i][j]]>1) ok=0;                if(usedcol[j][a[i][j]]>1) ok=0;            }        }//        for(int i=0;i<n;i++)//        if(curSumrow[i]>sumrow[i]) ok=0;//        for(int j=0;j<m;j++)//            if(curSumcol[j]>sumcol[j]) ok=0;        if(sumC!=sumR) ok=0;         sol=0;         cout<<ok<<endl;        if(ok)            Search(0,0);        printf("Case %d:\n",cas);        if(sol==0) cout<<"No answer."<<endl;        else if(sol>1) cout<<"Not unique."<<endl;        else{            for(int i=0;i<n;i++)            {                 for(int j=0;j<m-1;j++)                cout<<ans[i][j]<< ;                cout<<ans[i][m-1]<<endl;            }        }    }    return 0;}

四:总结

本题主要考察DFS的变式应用,以及对于设计DFS函数时剪枝技巧的利用。

 

中山大学校队选拔赛第一章题4【简单数迷Simple Kakuro】-------2015年1月28日