用深搜解决P1043

· · 题解

经过本蒟蒻无数次的提交,我用深搜 AC 这道题了!

解题思路

我们可以把写 2 个深搜,分别用来求最大值和最小值。设 dfs(id,last) 表示现在要处理第 id 个数,上一个数字是放在第 last 个区块里面。那么,我们就可以很轻松地写出程序了。但是有个细节,如果 step=n,他也可以放在第 1 个区块,因为他是一个环形的。

这个程序虽然会超时,但是只有第 4 个数据超时了,也能获得 80 分的好成绩。

#include<bits/stdc++.h>
using namespace std;
int n,m,a[51],sum[10],ans_max=INT_MIN,ans_min=INT_MAX;
bool flag[10];
int mod10(int x)
{
    return ((x%10)+10)%10;
}
void dfs1(int id,int last)
{
    int ans=1;
    for(int i=1;i<=m;i++)
    {
        if(flag[i])
        {
            ans*=mod10(sum[i]);
        }
    }
    if(id==n+1)
    {
        if(sum[m]!=0)
        ans_min=min(ans_min,ans);
        return;
    }
    if(id==1)
    {
        flag[1]=true;
        sum[1]=a[1];
        dfs1(2,1);
    }
    else
    {
        sum[last]+=a[id];
        dfs1(id+1,last);
        sum[last]-=a[id];
        if((last+1)<=m)
        {
            sum[last+1]+=a[id];
            flag[last+1]=true;
            dfs1(id+1,last+1);
            sum[last+1]-=a[id];
            flag[last+1]=false;
        }
        if(id==n)
        {
            sum[1]+=a[n];
            dfs1(n+1,1);
            sum[1]-=a[n];
        }
    }
}
void dfs2(int id,int last)
{
    int ans=1;
    for(int i=1;i<=m;i++)
    {
        if(flag[i])
        {
            ans*=mod10(sum[i]);
        }
    }
    if(id==n+1)
    {
        if(sum[m]!=0)
        ans_max=max(ans_max,ans);
        return;
    }
    if(id==1)
    {
        flag[1]=true;
        sum[1]=a[1];
        dfs2(2,1);
    }
    else
    {
        sum[last]+=a[id];
        dfs2(id+1,last);
        sum[last]-=a[id];
        if((last+1)<=m)
        {
            sum[last+1]+=a[id];
            flag[last+1]=true;
            dfs2(id+1,last+1);
            sum[last+1]-=a[id];
            flag[last+1]=false;
        }
        if(id==n)
        {
            sum[1]+=a[n];
            dfs2(n+1,1);
            sum[1]-=a[n];
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    dfs1(1,0);
    for(int i=1;i<=m;i++)
    {
        sum[i]=0;
        flag[i]=false;
    }
    dfs2(1,0);
    printf("%d\n%d",ans_min,ans_max);
    return 0;
}

最优性剪枝:求最大值时,如果当前的最优可能,也不如 ansmax,直接返回。最优可能是什么呢?就是把不确定的算出 9。要注意的是,第 last 个要算成 9,因为它还没结束,只是开始了。第 1 个区块也要算成 9,因为它后面可能因第 n 个数字而变化。当然,我们可以用同样的方法来对求最小值的深搜剪枝。

```cpp #include<bits/stdc++.h> using namespace std; int n,m,a[51],sum[10],ans_max=INT_MIN,ans_min=INT_MAX; bool flag[10]; int mod10(int x) { return ((x%10)+10)%10; } void dfs1(int id,int last) { int ans=1,cnt=1; for(int i=1;i<=m;i++) { if(flag[i]) { if(i!=1&&i!=last) cnt*=mod10(sum[i]); ans*=mod10(sum[i]); } } if(cnt>=ans_min) return; if(id==n+1) { if(sum[m]!=0) ans_min=min(ans_min,ans); return; } if(id==1) { flag[1]=true; sum[1]=a[1]; dfs1(2,1); } else { sum[last]+=a[id]; dfs1(id+1,last); sum[last]-=a[id]; if((last+1)<=m) { sum[last+1]+=a[id]; flag[last+1]=true; dfs1(id+1,last+1); sum[last+1]-=a[id]; flag[last+1]=false; } if(id==n) { sum[1]+=a[n]; dfs1(n+1,1); sum[1]-=a[n]; } } } void dfs2(int id,int last) { int ans=1,cnt=1; for(int i=1;i<=m;i++) { if(flag[i]) ans*=mod10(sum[i]); } for(int i=1;i<=m;i++) { if(i==1||flag[i]==false||i==last) cnt*=9; else cnt*=mod10(sum[i]); } if(cnt<=ans_max) return; if(id==n+1) { if(sum[m]!=0) ans_max=max(ans_max,ans); return; } if(id==1) { flag[1]=true; sum[1]=a[1]; dfs2(2,1); } else { sum[last]+=a[id]; dfs2(id+1,last); sum[last]-=a[id]; if((last+1)<=m) { sum[last+1]+=a[id]; flag[last+1]=true; dfs2(id+1,last+1); sum[last+1]-=a[id]; flag[last+1]=false; } if(id==n) { sum[1]+=a[n]; dfs2(n+1,1); sum[1]-=a[n]; } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); dfs1(1,0); for(int i=1;i<=m;i++) { sum[i]=0; flag[i]=false; } dfs2(1,0); printf("%d\n%d",ans_min,ans_max); return 0; } ```