洛谷P2329 [SCOI2005]栅栏 题解
这道题是去ZR的时候老师讲的,题目思路比较简单,就是二分答案,然后爆搜判断是否可行。对,就是枚举全排列的那种爆搜,但是时间复杂度显然不对,是(O(m^n)),但是好像只能这样做。
70分 直接爆搜
// luogu-judger-enable-o2
#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
const int M=55,N=1e3+100;
int n,m;
int a[M],b[N],a1[M],b1[N];
inline int dfs(int x,int n)
{
if (x==n+1) return 1;
bool p;
for (int i=1;i<=m;i++)
if (a1[i]-b1[x]>=0)
{
a1[i]-=b1[x];
p=dfs(x+1,n);
if (p) return 1;
a1[i]+=b1[x];
}
return p;
}
inline void pre()
{
for (int i=1;i<=m;i++)
a1[i]=a[i];
for (int i=1;i<=n;i++)
b1[i]=b[i];
return ;
}
inline bool cmp(int x,int y)
{
return x>y;
}
int main()
{
scanf("%d",&m);
for (int i=1;i<=m;i++)
scanf("%d",a+i);
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",b+i);
sort(b+1,b+n+1);
sort(a+1,a+m+1,cmp);
int l=0,r=n,mid=(l+r)>>1;
while (l<=r)
{
pre();
if (dfs(1,mid)) l=mid+1;
else r=mid-1;
mid=(l+r)>>1;
}
printf("%d",mid);
return 0;
}
然后还有一些剪枝
- 浪费的木板长度加上要拼的木板长度小于木板总长度,剪掉
- 当前要拼木板的长度和大于总木板长度,连搜都不用搜,直接剪掉
其实加上这两个剪枝就可以(AC),但是因为剪枝好像写假了,就得了80分。
然后我又倒着搜了一下,100分。
#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
const int M=55,N=1e3+100;
int n,m,tot,w;
int a[M],b[N],L[M],sum[N];
inline bool dfs(int x)
{
if (!x) return 1;
if (w+sum[x]>tot) return 0;
for (int i=1;i<=m;i++)
if (L[i]-b[x]>=0)
{
L[i]-=b[x];
if (L[i]<b[1]) w+=L[i];
if (dfs(x-1)) return 1;
L[i]+=b[x];
}
return 0;
}
inline bool check(int mid)
{
for (int i=1;i<=m;i++)
L[i]=a[i];
w=0;
if (sum[mid]>tot) return 0;
return dfs(mid);
}
int main()
{
scanf("%d",&m);
for (int i=1;i<=m;i++)
{
scanf("%d",a+i);
tot+=a[i];
}
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",b+i);
sort(a+1,a+m+1);
sort(b+1,b+n+1);
for (int i=1;i<=n;i++)
sum[i]=sum[i-1]+b[i];
int l=0,r=n,mid=(l+r)>>1,ans;
while (l<=r)
{
if (check(mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
mid=(l+r)>>1;
}
printf("%d
",ans);
return 0;
}
收获:
有时候倒着搜比较好,还可以少写一个变量,时间复杂度(O(玄学))