洛谷——P1120 小木棍 [数据加强版]

P1120 小木棍 [数据加强版]

题目描述

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050。

现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

搜索+剪枝

先贴一个70分的代码:

#include<bits/stdc++.h>

#define N 1010
using namespace std;

int n,a[100],tot,sum,sh[N];

bool vis[N],flg,pvis[100];

void scz(int k){
    for(int i=1;i<=k;i++) pvis[sh[i]]=1;
}

void dfs(int k,int l,int L){
    if(l==L){
        ++sum;
        if(sum*L==a[0]) flg=1;
        scz(k-1);
        return;
    }
    for(int i=1;i<=tot;i++){
        if(!vis[i]&&l+a[i]<=L&&!pvis[i]){
            vis[i]=1;sh[k]=i;
            dfs(k+1,l+a[i],L);
            vis[i]=0;
        }
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[++tot]);
        if(a[tot]>50) --tot;
//        a[0]+=a[tot];
        a[n+1]=max(a[n+1],a[tot]);
    }
    for(int i=0;i<=tot;i++)
        a[0]+=a[i];
//        ,printf("%d
",a[i]);
//    printf("%d
",a[0]);
    
    for(int i=a[n+1];i<=a[0];i++){
        memset(pvis,0,sizeof(pvis));
        if(a[0]%i==0){
            sum=0;
            dfs(0,0,i);
            if(flg){
                printf("%d
",i);
                break;
            }
        }
    }
    return 0;
}

剪枝大法好

#include<bits/stdc++.h>

#define N 1010
using namespace std;

int n,a[100],tot,sum,L,last;

bool vis[N];

bool dfs(int R,int M){//还剩下的木棍,需要拼凑的木棍长度 
    if(R==0&&M==0)
        return true;
    if(M==0) 
        M=L;
    int st=1;
    if(M!=L) 
        st=last+1;//剪枝4,从下一根更短的进行尝试 ,而不是从头开始 
    for(int i=st;i<=tot;i++){
        if(!vis[i]&&a[i]<=M){
            if(i>1&&vis[i-1]==false&&a[i]==a[i-1]){
                continue;
            }//剪枝1,如果上一个没被选中且当前这一个与上一个长度相等 
            vis[i]=1;last=i;
            if(dfs(R-1,M-a[i])) return true;
            else vis[i]=0;
            if(M==L||a[i]==M)
                return false;//剪枝2,如果第一根木棍不能更新,那么就永远不能更新 
                //剪枝3 如果当前木棍在==M的情况下不能成功,那就不可能成功 
        }
    }
    return false;
}

bool cmp(int x,int y) {
    return x>y;
}

int main() {
    scanf("%d",&n);
    for(int i=1; i<=n; i++) {
        scanf("%d",&a[++tot]);
        if(a[tot]>50) --tot;
        a[n+1]=max(a[n+1],a[tot]);
    }
    for(int i=0; i<=tot; i++)
        a[0]+=a[i];
    sort(a+1,a+1+tot,cmp);
    
    
    for( L=a[n+1]; L<=a[0]/2; L++) {
        memset(vis,0,sizeof(vis));
        if(a[0]%L) continue;
        if(dfs(tot,L)){
            printf("%d
",L);
            break;
        }
    }
    if(L>a[0]/2) printf("%d
",a[0]);
    return 0;
}

剪枝方法总结:

1) 要选择合适的搜索顺序 如果一个任务分为 A, B, C…..等步骤(先后次序无关), 要优先尝试可能性少的步骤

2) 要发现表面上不同,实质相同的重复状态,避免重复的搜索

3) 要根据实际问题发掘剪枝方案

原文地址:https://www.cnblogs.com/song-/p/9636019.html