P1120 小木棍 [数据加强版]

小木棍[数据加强版]

题意:对于若干个小木棒,将它们拼接成若干个长木棒,使得所有长木棒的长度相等,输出长木棒最短的可能长度

思路:(深搜+剪枝)

1.考虑到长木棍的可能长度在最长小木棍长度与所有小木棍总长度之间,先打一个暴力搜索,在这个范围内dfs找答案,显然会TLE,估计有21分左右。
2.长木棍的数目为整数,所以长木棍的长度为总长度的约数。
3.如果当前处理的木棍长度加上最短的小木棒长度都超过了搜索的答案,显然不存在,剪枝。
4.如果剩余总长度加上当前处理的木棍长度都达不到目标长度,剪枝。
5.更短的木棒比长木棒更加灵活,所以对小木棒长度进行排序后从大到小枚举。
6.因为木棒在排序后有序,所以每次处理的木棒一定在上一根处理的木棒之后,传递上一根木棒的位置。
*7.(重要剪枝)某次操作中如果已经完成了一根完整的长木棍,而在更深层次的dfs中又得出答案错误,则当前循环可以直接跳出,因为对后续更短的木棒进行操作会有重复计算,剪枝。

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
//Mystery_Sky
//
#define M 1000100
#define INF 0x3f3f3f3f
#define ll long long
inline int read()
{
	int x=0, f=1;
	char c = getchar();
	while(c < '0' || c > '9') {if(c == '-') f=-1; c=getchar();}
	while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c=getchar();}
	return x * f;
}
int n, tot, l, r;
int a[M];//每根小木棍的长度。 
bool flag = false, vis[M];
int minn = INF;

inline bool check_1(int sum, int goal)
{
	if(goal - sum < minn) return true;
	else return false;
}

void dfs(int depth, int sum, int num, int goal, int rest, int pre)//已取了多少小木棒,当前在拼的小木棒总长,已经拼好了多少该长度的小木棒,目标长度,总共还剩多长,上一段在哪。 
{
	if(flag) return;
	if(depth >= tot) {
		if(sum == 0) flag = true;
		return; 
	}
	if(check_1(sum, goal)) return;//剪枝1 -> 21分
	if(rest + sum < goal) return;//剪枝5 -> 39分 
	for(int i = 1; i <= tot; i++) {//剪枝6 -> 39分 
		if(!vis[i]) {
			if(sum + a[i] > goal) return;
			break;
		}
	}
	for(int i = pre; i >= 1; i--) {
		if(flag) return;
		if(!vis[i]) {
			//if(a[i] == a[i+1] && vis[i+1] == false) continue;//剪枝4 ->39分 
			if(sum + a[i] < goal) {
				vis[i] = 1;
				dfs(depth+1, sum+a[i], num, goal, rest - a[i], i);//剪枝7->57分 
				vis[i] = 0;
			}
			else if(sum + a[i] == goal) {
				vis[i] = 1;
				dfs(depth+1, 0, ++num, goal, rest - a[i], tot);
				vis[i] = 0;
			}
			if(sum + a[i] == goal || sum == 0) break;//*重要剪枝:57分以外的53分。 
			while(a[i] == a[i-1]) i--;
		}
	}
}

int main() {
	n = read();
	bool s = true;
	for(int i = 1; i <= n; i++) {
		int x = read();
		if(x <= 50) {
			a[++tot] = x, l = max(l, a[tot]), r = r + x;
			minn = min(a[tot], minn);
			if(a[tot] != a[tot-1]) s = false;
		} 
	}
	if(s) {
		printf("%d\n", a[1]);
		return 0;
	}
	sort(a+1, a+1+tot);
	for(int i = l; i <= r; i++) {//剪枝2 -> 33分 
		if(i != l && l + minn > i) continue;//剪枝3 -> 36分 
		if(r % i == 0) {
			dfs(0, 0, 0, i, r, tot);
			if(flag) {
				printf("%d\n", i);
				break;
			}
		}
	}
	return 0;
}

剪枝真快乐

posted @ 2019-08-07 20:04  Mystery_Sky  阅读(164)  评论(0编辑  收藏  举报