Executing... Test 1: TEST OK [0.005 secs, 3392 KB] Test 2: TEST OK [0.005 secs, 3392 KB] Test 3: TEST OK [0.008 secs, 3392 KB] Test 4: TEST OK [0.097 secs, 3392 KB] Test 5: TEST OK [0.005 secs, 3392 KB] Test 6: TEST OK [0.062 secs, 3392 KB] Test 7: TEST OK [0.005 secs, 3392 KB] Test 8: TEST OK [0.011 secs, 3392 KB] Test 9: TEST OK [0.030 secs, 3392 KB] Test 10: TEST OK [0.005 secs, 3392 KB] Test 11: TEST OK [0.005 secs, 3392 KB] Test 12: TEST OK [0.008 secs, 3392 KB] All tests OK.
二分答案! 我真的没想到,看了nocow的题解。
把need从小到大排序
1、假设前k个need可以得到,那么首先可以知道前k个need木板的总和,也就可以计算出需要浪费的木板总量tot_wast。
因为已经精确到要浪费的总量了,某个切割方案让浪费的总量超过tot_wast,显然是无解的
2、因为只有128种数字,却有1000多木板,所以各种木板是重复的大小,这就有一个搜索次序的情况了。 比如有2个need木板都是5,5.
在当前的give的木板上,我切后一块,不要前一块。 这样会造成大量的重复运算,所以需要用一些方法来处理掉这些多余的计算量。
3、尝试要哪些need的时候,要从大到小!
for (int i = max_need; i >=0 ; -- i)
{
if (vis[i]) continue;
if (len < need[i]) continue; //不够切的,也直接continue
vis[i] = 1;
//cut_num[i] = k; // i是被k切的
if (i && need[i] == need[i - 1] && !vis[i - 1])
{
vis[i] = 0;
continue;
}
flag = dfs(k, len - need[i], get + 1, wast);
if (flag) return true;
vis[i] = 0;
IFUSD =true;
}
for (int i = max_need; i >=0 ; -- i)
改为
for (int i = 0; i <= <span style="font-family: Arial, Helvetica, sans-serif;">max_need</span> ; ++ i)
时间差距巨大无比! 后者跑了20秒都不出答案……前者就快多了.
原因? 数据的原因嘛?我不认为如此。
首先众所周知,索树在开始的树枝被剪的效果永远比在结尾被剪效果来的好~
试大的,会很大程度的造成【快速浪费】,根据前面的浪费条件的剪枝原理,快速剪掉很多,快速出解。
/*
TASK:fence8
LANG:C++
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
int gived_num, need_num;
int gived[55], need[1050];
int sum[1050]={0};
int tot_give(0);
void init()
{
scanf("%d", &gived_num);
for (int i = 0; i != gived_num; ++ i) scanf("%d", gived + i);
for (int i = 0; i != gived_num; ++ i) tot_give += gived[i];
scanf("%d", &need_num);
for (int i = 0; i != need_num; ++ i) scanf("%d", need + i);
sort(need, need + need_num);
sum[0] = need[0];
for (int i = 1; i != need_num; ++ i) sum[i] = need[i] + sum[i - 1]; //合计所用的材料
}
int max_wast, max_need;
bool vis[1050] = {false};
int cut_num[1050];
bool dfs(int k, int len, int get, int wast) //当前切第k个木料,第k个木料还剩多少长度, 浪费的总量
{
if (wast > max_wast) return false; //过度浪费,不可能成功,失败
if (k == gived_num) return false;
if (get == max_need)
{
//cout<<"成功"<<endl;
return true;
}
bool flag = false;
bool IFUSD = false;
for (int i = max_need; i >=0 ; -- i)
{
if (vis[i]) continue;
if (len < need[i]) continue; //不够切的,也直接continue
vis[i] = 1;
//cut_num[i] = k; // i是被k切的
if (i && need[i] == need[i - 1] && !vis[i - 1])
{
vis[i] = 0;
continue;
}
flag = dfs(k, len - need[i], get + 1, wast);
if (flag) return true;
vis[i] = 0;
IFUSD =true;
}
//下一块木料,这个不用了
if (IFUSD) return false; //这块能切割,但是不切割,肯定是不行的,false
flag = dfs(k + 1, gived[k + 1], get, wast + len);
if (flag) return true;
return false;
}
bool check(int k) //前k个need全部制作出来
{
memset(vis, 0, sizeof(vis));
memset(cut_num, 0, sizeof(cut_num));
sum[k]; //合计使用的材料
max_wast = tot_give - sum[k]; //正好要浪费这么多材料,如果浪费更多,显然是不对的
//cout<<tot_give<<" "<<sum[20]<<endl;
if (sum[k] > tot_give) return false;
max_need = k;
return dfs(0, gived[0], 0, 0);
}
void doit()
{
int max_gived=0;
for (int i = 0; i != gived_num; ++ i) if (gived[i] > max_gived) max_gived = gived[i];
if (max_gived < need[0])
{
printf("0\n");
return;
}
int left = 0, right = need_num; // [);
int mid;
while (left + 1 < right) //序号[left, right)合法
{
mid = (left + right) / 2;
if (check(mid)) left = mid;
else right = mid;
}
cout << left + 1 << endl;
}
int main()
{
freopen("fence8.in","r",stdin);
freopen("fence8.out","w",stdout);
init();
doit();
return 0;
}