中山大学校队选拔赛第一章题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 1000000000
int 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函数时剪枝技巧的利用。

 

posted @ 2015-01-28 12:45  future_hero  阅读(243)  评论(0编辑  收藏  举报