您的位置:首页 > Web前端

usaco4.12Fence Rails(迭代加深)

2013-08-28 19:36 141 查看
为了这题还去学了下迭代加深 回来还是不会写

只好参考各大神的代码及题解了

二分枚举最大可以切的块数 然后就是各种分析及优化

USACO题解里写了7个优化。。

问题分析

抽象一下就可以发现,算法的本质是多重背包问题。 补充:这题与破锣乐队都是多个背包,不可重复放物品。区别在于破锣乐队要有顺序,此题不需要,这样此题就必须要搜索才行。 单个背包的问题我们可以用DP解决,但是对于这种问题我们只能用搜索了。 但是可以看一看这道题的数据规模:1<=n<=50,1<=r<=1023。如此大的规模我们只能考虑进一步的优化。

我采用的是dfsid搜索每一个rail来源的board。以下技巧都是针对这种搜索顺序来制定的。 (注:rail是所需要切成的东西,board是供应商提供的原料)

如果使用dancing links的话,可以让程序的常数快2倍。

优化技巧

很容易就能注意到,由于每块rail的价值是相等的——也就是说切小的要比切大的来的划算。那么我们在搜索能否切出i个rail的方案是自然要选最小的i个rail来切。

经过一些实验可以发现,先切大的rail比先切小的rail更容易提前出解。同样,[先切小的board比先切大的board更容易提前出解?]{注:好像先切大的board要比先切小的更快}。{*我的程序先切小再切大第5个点就TLE了,而先切大再切小就快很多,见C++程序.{跟我一样,握个手,一定要先大后小!!!}}

由于r最大可能是1023,但是rail长度的范围却只有0~128,这点提醒了我们有很多rail的长度会是相同的。所以我们要避免冗余,优化搜索顺序。若有rail[i+1]=rail[i],则rail[i+1]对应的board一定大于等于rail[i]对应的board。可以通过这种方法剪掉很多冗余的枝条。

相应的,如果board[i]=board[i+1],那么从board[i]切下的最大的rail一定大于等于从board[i+1]切下的最大的rail。

对于切剩下的board(无法再切下rail),统计一下总和。如果这个值大于board长度的总和减去rail长度的总和,一定无解,可以剪枝。这个剪枝最关键。

二分答案

其实在读入的过程中,如果rail[i] > max{board} 那么这个rail应该舍去 By Clarkok

/*
ID: shangca2
LANG: C++
TASK: fence8
*/
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stdlib.h>
using namespace std;
int bo[55],te[55],ra[1050],v;
int n,m,res,s,sum[1050],flag;
void dfs(int tt,int st)
{
int i;
if(flag) return ;
if(tt==0)//全部可以切完
{
flag = 1;
return ;
}
int ss=0,o;
for(i = 1 ; i <= n ; i++)//当前木板剩余值已经大于最多的剩余值 肯定不用再搜
{

if(te[i]<ra[1])
ss+=te[i];
}
if(ss>v) return ;
for(i = st ; i <= n ; i++)
{
if(te[i]>=ra[tt])
{

if(tt-1>=1&&ra[tt]==ra[tt-1])//一个小优化 如果ra[tt]已经在i~N里搜了 那么前面跟它相等的 就不会在i之前搜了
o = i;
else
o = 1;
te[i]-=ra[tt];
dfs(tt-1,o);
te[i]+=ra[tt];
}
}
return ;
}
int main()
{
//freopen("fence8.in","r",stdin);
//freopen("fence8.out","w",stdout);
int i;
cin>>n;
for(i = 1 ; i <= n ; i++)
{
scanf("%d",&bo[i]);
s+=bo[i];
}
sort(bo+1,bo+n+1);
for(i = 1; i <= n ; i++)
te[i] = bo[i];
scanf("%d",&m);
for(i = 1; i <= m ; i++)
{
scanf("%d",&ra[i]);

}
sort(ra+1,ra+m+1);
for(i = 1; i <= m ; i++)
sum[i] = sum[i-1]+ra[i];
int low=0,high = m;
while(low<high)//二分找一下 满足最多可以切多少块
{
int mm = (low+high+1)>>1;
v = s-sum[mm];
flag = 0;
dfs(mm,1);
if(flag)
low = mm;
else
high = mm-1;
}
printf("%d\n",low);
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: