Luogu P1549 棋盘问题(2)

时间:2023-03-09 09:30:41
Luogu  P1549 棋盘问题(2)

题意

在N×N的棋盘上(1≤N≤10),填入1,2,…,N^2,共N^2个数,使得任意两个相邻的数之和为素数。

思路

先线性筛(非标准版),然后用a数组记录以i为下标的数是不是质数(就是标记数组),然后就可以搜了,加个vis数组记录此数有没有被搜过。
用f数组记录当前答案(因为第一个搜到的不一定是最优的),用sum记录第一行与第一列的和,如果大于MINN就退出。
然后你就会发现过不了。
接下来就是玄学剪枝了,加个TIM记录答案更新时间,如果长时间未更新就直接退出,然后之后不用输出,但如果可以搜出就等dfs完再输出。
提前判断NO情况只是为了加快速度而已(想拿第一页),但谁知都是神仙打表巨佬(0ms的那种)。
细节看代码
依旧n==10跑不动辣

代码

#include<bits/stdc++.h>
using namespace std;
int f[15][15],ans[15][15],a[205],n,vis[105],MINN=999999,TIM=0,flag=0;//a开这么多因为n*n*2-1最大只有199
inline void dfs(int step)
{
    if (step>n*n)
    {
    TIM++;
    if (TIM>10)
    {
        flag=1;
        for (int i=1;i<=n;i++)
         {
            for (int j=1;j<=n;j++)
             cout<<ans[i][j]<<" ";
             cout<<endl;
         }
         exit(0);
         return;//多余,个人习惯
    }
    int sum=0;
    for (int i=1;i<=n;i++) sum+=f[1][i]+f[i][1];
    if (MINN<=sum) return;//80分看过来
    MINN=sum;
    TIM=0;
    for (int i=1;i<=n;i++)for (int j=1;j<=n;j++) ans[i][j]=f[i][j];
    return;
    }
    int x=(step-1)/n+1;
    int y=(step-1)%n+1;//常规,本人习惯这么打
    for (int i=1;i<=n*n;i++)
    {
        if ((a[f[x-1][y]+i]==1||f[x-1][y]==-1)&&(a[f[x][y-1]+i]==1||f[x][y-1]==-1)&&(a[f[x+1][y]+i]==1||f[x+1][y]==-1)&&(a[f[x][y+1]+i]==1||f[x][y+1]==-1)&&vis[i]==0)//看,很方便吧(逃
        {
        vis[i]=1;
        f[x][y]=i;
        dfs(step+1);
        f[x][y]=-1;
        vis[i]=0;
        }
    }
}
void slove()
{
 int m=2*n*n;
 for (int i=2;i<=m;i++) a[i]=1;
 a[1]=0;
 for (int i=2;i<=m;i++) if (a[i]==1) for (int j=2;j<=m/i+1;j++) a[i*j]=0;
 //for (int i=1;i<=n;i++) cout<<i<<" "<<a[i]<<endl;
}
int main()
{
 //freopen("1.out","w",stdout);
 cin>>n;
 if (n==1||n==3||n==9) {cout<<"NO"<<endl;return 0;}
 slove();
 for (int i=0;i<=n+1;i++)for (int j=0;j<=n+1;j++)f[i][j]=-1;//这样判断边缘要方便许多,但如果n==1要特判。。。
 dfs(1);
 if (flag==0)
 {
    for (int i=1;i<=n;i++)
         {
            for (int j=1;j<=n;j++)
             cout<<ans[i][j]<<" ";
             cout<<endl;
         }
 }
 return 0;
}