zoukankan      html  css  js  c++  java
  • POJ1011 Sticks

    小木棍

    dfs 剪枝

    • 把所有木棍从大到小排序 优化搜索顺序(较大的木棍更不容易凑出需要的长度,使之后的剪枝更早发生)

    • 枚举可能的原始木棍长度,(注意这里不满足单调性,不能二分答案),这里可以把最长的一根现有木棍作为枚举的下界,上界为所有木棍更总长度。

    • 答案只可能为总长度的因数,在枚举过程中其他的数可直接排除。

    • dfs过程中,若剩下还要拼的长度比第n根碎木棍(最短的那根)还短,那么一定拼不成。

    • 若有几根相同长的木棍,其中拿出一根拼失败了,剩下的也一定会失败,不用再dfs了(这处剪枝很重要,因为每根木棍长度在50以内,会有很多重复长度)。

    • 对于当前枚举的原始木棍长度,如果第一次用到的碎木棍拼失败了,那么这个原始木棍长度不合适,直接return。因为那根碎木棍无论放在哪都不合适。

    • 对于当前枚举的原始木棍长度,如果有一次选用的碎木棍恰能把那根拼起来,但dfs递归之后却失败了,那么直接return。因为贪心的想,木棍按长度从大到小排,这次用上恰能补上空隙的长木棍之后都失败了,那么如果用较短的木棍去补这个空隙,之后不可能更优。

      丑陋的代码

      #include <iostream>
      #include <cstdio>
      #include <cstring>
      #include <algorithm>
      #include <cstdlib>
      using namespace std;
      const int maxn=100;
      int low,sum,n,len,cnt,tot;
      int a[maxn];
      bool used[maxn],judge;
      inline bool cmp(const int &a,const int &b){
          return a>b;
      }
      void dfs(int now,int sum,int last){
      //now--正在拼第几根原始木棍 sum--当前正在拼的这根已经拼好的长度 
      //last--上次选择的木棍编号(便于剪枝) 
          if(now>cnt){
              judge=true;
              return;
          }
          if(sum==len) dfs(now+1,0,1);
          if(len-sum<a[n]) return;//剪枝,若剩下还要拼的长度比第n根碎木棍(最短的那根)还短,那么一定拼不成
          int fail=0;//上次拼失败的木棍长度 
          for(int i=last;i<=n;++i){
              if(used[i]||sum+a[i]>len||a[i]==fail) continue;
              used[i]=true;
              dfs(now,sum+a[i],i+1);
              if(judge) return;//若已经成功就没必要再拼了 
              used[i]=false;//回溯 
              fail=a[i];
              if(sum==0||sum+a[i]==len) return;
          }
      }
      int main(){
          while(scanf("%d",&n) && n){
              low=sum=tot=0;
      	    for(int i=1,x;i<=n;++i){
      	        scanf("%d",&x);
      	        if(x<=50){  
      	        	a[++tot]=x;
      	        	low=max(low,x);//预估下界
      	        	sum+=x;
      			}
      	    }
      	    n=tot;
              sort(a+1,a+1+n,cmp);//优化搜索顺序
              for(len=low;len<=sum;++len){
                  if(sum%len) continue;//答案只可能为总长度的因数 
                  memset(used,0,sizeof(used));//每次都要重置 
                  judge=0;
                  cnt=sum/len;
                  dfs(1,0,1);
                  if(judge) break;
              }
              printf("%d
      ",len);
          }
          return 0;
      }
      

      dfs最好写返回值为bool类型的,如蓝书(李煜东《算法竞赛进阶指南》)代码:

      #include<iostream>
      #include<cstdio>
      #include<cstring>
      #include<algorithm>
      using namespace std;
      int a[100], v[100], n, len, cnt;
      
      // 正在拼第stick根原始木棒(已经拼好了stick-1根)
      // 第stick根木棒的当前长度为cab
      // 拼接到第stick根木棒中的上一根小木棍为last
      bool dfs(int stick, int cab, int last) {
      	// 所有原始木棒已经全部拼好,搜索成功
      	if (stick > cnt) return true;
      	// 第stick根木棒已经拼好,去拼下一根
      	if (cab == len) return dfs(stick + 1, 0, 1);
      	int fail = 0; // 剪枝(2)
      	// 剪枝(1):小木棍长度递减(从last开始枚举)
      	for (int i = last; i <= n; i++)
      		if (!v[i] && cab + a[i] <= len && fail != a[i]) {
      			v[i] = 1;
      			if (dfs(stick, cab + a[i], i + 1)) return true;
      			fail = a[i];
      			v[i] = 0; // 还原现场
      			if (cab == 0 || cab + a[i] == len) return false; // 剪枝(3,4)
      		}
      	return false; // 所有分支均尝试过,搜索失败
      }
      
      int main() {
      	while (cin >> n && n) {
      		int sum = 0, val = 0, m = 0;
      		for (int i = 1; i <= n; i++) {
      			int x;
      			scanf("%d", &x);
      			if (x <= 50) {
      				a[++m] = x;
      				sum += a[m];
      				val = max(val, a[m]);
      			}
      		}
      		n = m;
      		sort(a + 1, a + n + 1);
      		reverse(a + 1, a + n + 1); 
      		for (len = val; len <= sum; len++) {
      			if (sum % len) continue;
      			cnt = sum / len; // 原始木棒长度为len,共cnt根
      			memset(v, 0, sizeof(v));
      			if (dfs(1, 0, 1)) break;
      		}
      		cout << len << endl;
      	}
      }
      

  • 相关阅读:
    微服务
    Ubunt16.04下安装PHP7+Nginx+MySQL
    Ubuntu Linux 14.04 LTS 上安装php7+mysql+nginx
    magento2 重置后台密码
    crontab命令
    解决linux buffer/cache 消耗内存过高引发的问题
    Linux配置自动发送邮件
    buff/cache 内容释放
    利用Linode面板Clone克隆搬家迁移不同VPS数据及利用IP Swap迁移IP地址
    css3动画(从上、左下、左、右进入页面)
  • 原文地址:https://www.cnblogs.com/yu-xing/p/10363724.html
Copyright © 2011-2022 走看看