您的位置:首页 > 其它

【总结】搜索的剪枝二分预处理和离散化等优化

2012-10-29 20:43 218 查看
送给圣诞夜的贺卡

位运算加速,因为集合的总元素个数较少,可以用位运算加速。

降序排序,使得越到后面S降得越快,让最后S很小,(参考黑书的剪枝那一节),因为到一般剪枝都在搜索的深层,所以容易被剪掉。反之,如果升序排序,则后面的S仍较大,不容易满足if (sum + S[u] <= ans)这个条件,也就不容易被剪掉。

极限化,如果剩余的都以最优情况决策,得到的解如果仍然不满足题意,则可以剪掉,达到加速。

手动调整决策方向,如此题中的没有冲突的,必须选择。

#include <cstring>
#include <cstdio>
#include <string>
#include <algorithm>
using std::sort;

typedef long long ll;
ll conflict[60];
long S[60];
bool wc[60];
long hash[60];
long v[60];
long n;
long ans = 0;

void dfs(long u,ll s,long sum)
{
if (u == n+1)
{
if (sum > ans)
ans = sum;
return;
}
if (sum + S[u] <= ans)
return;
if (!(s&conflict[u]))
dfs(u+1,s|(1ll<<u),sum+v[u]);
if (wc[u])
dfs(u+1,s,sum);
}

long getint()
{
long rs=0;bool sgn=1;char tmp;
do tmp = getchar();
while (!isdigit(tmp)&&tmp!='-');
if (tmp=='-'){tmp=getchar();sgn=0;}
do rs=(rs<<3)+(rs<<1)+tmp-'0';
while (isdigit(tmp=getchar()));
return sgn?rs:-rs;
}

struct node
{
long v;
long i;
bool operator<(const node& n2)const
{
return v > n2.v;
}
};
node a[60];

int main()
{
freopen("gift.in","r",stdin);
freopen("gift.out","w",stdout);

n = getint();

for (long i=1;i<n+1;i++)
{
a[i].v = getint();
a[i].i = i;
}
sort(a+1,a+1+n);
for (long i=1;i<n+1;i++)
{
hash[a[i].i] = i;
v[i] = a[i].v;
}
for (long i=n;i>0;i--)
S[i] = S[i+1]+v[i];

long u,v;
while (scanf("%ld%ld",&u,&v)==2)
{
u = hash[u];
v = hash[v];
conflict[u] |= (1ll<<v);
conflict[v] |= (1ll<<u);
wc[u] = true;
wc[v] = true;
}

dfs(1,0,0);

printf("%ld",ans);
return 0;
}


丛林探险

二分优化,时间总和不超过12000000,二分次数最多24次。如果能成功,就修改mid为s,这样还可以减少很多二分次数。剪枝都不需要。

#include <cstdio>
#include <string>
#include <cstring>

long s,t,k;
long n;
long mid;
bool used[5010];

struct node
{
long ind;
node* nxt;
long c;
long d;
};
node* head[5010];

long getint()
{
long rs=0;bool sgn=1;char tmp;
do tmp=getchar();
while (!isdigit(tmp)&&tmp-'-');
if (tmp=='-'){tmp=getchar();sgn=0;}
do rs=(rs<<3)+(rs<<1)+tmp-'0';
while (isdigit(tmp=getchar()));
return sgn?rs:-rs;
}

void insert(long a,long b,long c,long d)
{
node* nn = new node;
nn -> ind = b;
nn -> nxt = head[a];
nn -> c = c;
nn -> d = d;
head[a] = nn;
}

bool can(long u,long e,long s)
{
if (s > mid)
return false;
if (u == t)
{
if (mid > s)
mid = s;
return true;
}
for (node* vv=head[u];vv;vv=vv->nxt)
{
long v = vv->ind;
if (!used[v])
{
if (e-vv->c >= 0)
{
used[v] = true;
if (can(v,e-vv->c,s+vv->d))
return true;
used[v] = false;
}
}
}
return false;
}

int main()
{
freopen("forest.in","r",stdin);
freopen("forest.out","w",stdout);

n = getint();
long m = getint();
long sumd = 0;

for (long i=1;i<m+1;i++)
{
long x = getint();
long y = getint();
long c = getint();
long d = getint();
insert(x,y,c,d);
insert(y,x,c,d);
sumd += d;
}
s = getint();
t = getint();
k = getint();

long ans = 0x3f3f3f3f;
long l = 0;
long r = sumd;
while (l <= r)
{
mid = (l+r)>>1;
memset(used,0,sizeof used);
used[s] = true;
if (can(s,k,0))
{
if (ans > mid)
ans = mid;
r = mid-1;
}
else
{
l = mid+1;
}

}
if (ans == 0x3f3f3f3f)
printf("-1");
else
printf("%ld",ans);
return 0 ;
}


软件下载

二分优化。这道题按照降序排序,因为所有的软件要下载完!!!(如果不一定下载完,当然就应该升序排序)这样就能尽早地剪掉大枝。

极限化,假设所有剩下的电脑刚好用掉所有能用的时间,如果还不能将所有下载时间进行完,剪掉。

#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <functional>
using std::sort;
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))

long m,n;
long mid;
long a[60];
long cnt[20];
bool used[60];

bool can(long u,long v,long left)
{
if ((n-u+1)*mid-cnt[u] < left)
return false;
if (v == m)
return true;

for (long i=1;i<m+1;i++)
{
if (!used[i])
{
if (cnt[u] + a[i] <= mid)
{
used[i] = true;
cnt[u] += a[i];
if (can(u,v+1,left-a[i])) return true;
cnt[u] -= a[i];
used[i] = false;
}
else if (u < n)
{
used[i] = true;
cnt[u+1] += a[i];
if (can(u+1,v+1,left-a[i])) return true;
cnt[u+1] -= a[i];
used[i] = false;
}
else return false;
}
}
return false;
}

long getint()
{
long rs=0;bool sgn=1;char tmp;
do tmp = getchar();
while (!isdigit(tmp)&&tmp-'-');
if (tmp == '-'){tmp=getchar();sgn=0;}
do rs=(rs<<3)+(rs<<1)+tmp-'0';
while (isdigit(tmp=getchar()));
return sgn?rs:-rs;
}

int main()
{
freopen("soft.in","r",stdin);
freopen("soft.out","w",stdout);

n = getint();
m = getint();

long l = -0x3f3f3f3f;
long r = 0;
long sum = 0;
for (long i=1;i<m+1;i++)
{
a[i] = getint();
r += a[i];
l = max(l,a[i]);
}
sum = r;
sort(a+1,a+1+m,std::greater<long>() );

long ans = 0x3f3f3f3f;
while (l <= r)
{
mid = (l+r) >> 1;
memset(used,0,sizeof used);
memset(cnt,0,sizeof cnt);
used[1] = true;
cnt[1] = a[1];
if (can(1,1,sum-a[1]))
{
if (ans > mid)
ans = mid;
r = mid-1;
}
else
l = mid+1;
}

printf("%ld",ans);
return 0;
}


核反应,和Soft一样的搜索方式,类似的剪枝,类似的排序。转换一下模型就是上面有一些小东西,要装到下面的一些大小固定的桶里,刚好装满,求能否成功。时间复杂度为O(m)

注意因为所有的都得装进去才能算作成功,因此不存在所谓的尽早找到可行解然后返回true的说法,只有尽早找出不符合题意的情况剪掉,因此上面的物品要降序排序,下面的桶升序排序。

#include <cstdio>
#include <map>
#include <string>
#include <iostream>
#include <functional>
#include <algorithm>

using std::sort;
using std::greater;
using std::make_pair;
using std::cin;
using std::map;
using std::string;
string tmp;
map<string,long> hash;

char str[100][5] =
{"H","He","Li","Be","B","C","N","O","F","Ne",

"Na","Mg","Al","Si","P","S","Cl","Ar","K","Ca",

"Sc","Ti","V","Cr","Mn","Fe","Co","Ni","Cu",

"Zn","Ga","Ge","As","Se","Br","Kr","Rb","Sr",

"Y","Zr","Nb","Mo","Tc","Ru","Rh","Pd","Ag",

"Cd","In","Sn","Sb","Te","I","Xe","Cs","Ba",

"La","Ce","Pr","Nd","Pm","Sm","Eu","Gd",

"Tb","Dy","Ho","Er","Tm","Yb","Lu","Hf","Ta",

"W","Re","Os","Ir","Pt","Au","Hg","Tl","Pb",

"Bi","Po","At","Rn","Fr","Ra","Ac","Th","Pa",

"U","Np","Pu","Am","Cm","Bk","Cf","Es","Fm"};

bool used[20];
long a[20];
long b[20];
long m,n;

bool dfs(long u,long v)
{
if (v == m+1)
{
return true;
}
for (long i=1;i<m+1;i++)
{
if (!used[i])
{
used[i] = true;
if (a[u] >= b[i])
{
a[u] -= b[i];
if (dfs(u,v+1))
return true;
a[u] += b[i];
}
used[i] = false;
}
else if (!a[u] && u<n)
{
used[i] = true;
if (a[u+1] >= b[i])
{
a[u+1] -= b[i];
if (dfs(u+1,v+1))
return true;
a[u+1] += b[i];
}
used[i] = false;
}
}
return false;
}

int main()
{
freopen("nuclear.in","r",stdin);
freopen("nuclear.out","w",stdout);

for (long i=0;i<100;i++)
hash.insert(make_pair(str[i],i+1));

while (scanf("%ld%ld",&m,&n) == 2)
{
memset(used,0,sizeof used);
long sa = 0;
long sb = 0;
for (long i=1;i<m+1;i++)
{
cin >> tmp;
b[i] = hash[tmp];
sb += b[i];
}
for (long i=1;i<n+1;i++)
{
cin >> tmp;
a[i] = hash[tmp];
sa += a[i];
}

if (sa != sb)
{
printf("NO\n");
continue;
}
sort(b+1,b+1+m,greater<long>());
sort(a+1,a+1+n);
if (dfs(1,1))
printf("YES\n");
else
printf("NO\n");
}
return 0;
}


Cake,离散化优化搜索,使复杂度只与存在的点有关。

#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
using std::min;
using std::sort;
using std::max;

long n,k,m;
long left[100010];
long right[100010];
long top[100010];
long bottom[100010];
long sum[100010];
long fa[100010];

struct POS
{
long x;
long y;
bool operator<(const POS& p2)const
{
if (x == p2.x)
return y<p2.y;
return x<p2.x;
}
};
POS pos[100010];

long getroot(long u)
{
if (fa[u] == u)
return fa[u];
return fa[u] = getroot(fa[u]);
}

void merge(long a,long b)
{
long root = getroot(b);
long rt = getroot(a);
if (root != rt)
{
fa[rt] = root;
bottom[root] = max(bottom[root],bottom[rt]);
top[root] = min(top[root],top[rt]);
left[root] = min(left[root],left[rt]);
right[root] = max(right[root],right[rt]);
sum[root] += sum[rt];
}
}

long getint()
{
long rs=0;bool sgn=1;char tmp;
do tmp=getchar();
while (!isdigit(tmp)&&tmp-'-');
if (tmp=='-'){tmp=getchar();sgn=0;}
do rs=(rs<<3)+(rs<<1)+tmp-'0';
while (isdigit(tmp=getchar()));
return sgn?rs:-rs;
}

void next(long u,long &v)
{
v = u;
while (v < k && pos[v+1].x == pos[v].x)
{
if (pos[v+1].y == pos[v].y+1)
merge(v,v+1);
v ++;
}
}

void search()
{
long u1 = 1;
long v1;
next(u1,v1);
long u2 = v1+1;
long v2;
next(u2,v2);

while (v2 <= k)
{
if (pos[u2].x-pos[u1].x == 1)
{
long i = u1;
long j = u2;
while (i<v1+1 && j<v2+1)
{
if (abs(pos[i].y-pos[j].y)<=1)
merge(i,j);
if (pos[i].y<pos[j].y)
i ++;
else
j ++;
}
}
u1 = u2;
v1 = v2;
u2 = v1+1;
next(u2,v2);
}

long ans = 0;
for (long i=1;i<k+1;i++)
{
if (fa[i] == i)
{
if ((left[i]>1&&right[i]<m && top[i]>1&&bottom[i]<n)
&& ((right[i]-left[i]+1)*(bottom[i]-top[i]+1) == sum[i]))
{
ans ++;
}
}
}

printf("%ld",ans);
}

int main()
{
freopen("cake.in","r",stdin);
freopen("cake.out","w",stdout);

n = getint();
m = getint();
k = getint();

for (long i=1;i<k+1;i++)
fa[i] = i;
for (long i=1;i<k+1;i++)
{
pos[i].x = getint();
pos[i].y = getint();
}
sort(pos+1,pos+1+k);
long _k = 0;
for (long i=1;i<k+1;i++)
{
if (pos[i].x!=pos[i-1].x||pos[i].y!=pos[i-1].y)
{
_k ++;
left[_k] = right[_k] = pos[_k].y;
top[_k] = bottom[_k] = pos[_k].x;
sum[_k] = 1;
}
}
k = _k;

search();
return 0;
}


等差数列

预处理优化,因为对于任意一个当前点u,它向后找到的下一个点v的权值都是固定的。

因此可以预处理出权值d最后出现的位置hash[d]。

再加上一个极限化的最优化剪枝。

#include <cstdio>
#include <algorithm>
using std::max;
using std::lower_bound;
using std::sort;

long hash[10001110];
long num[5010];
long ans = 1;
long n;

void dfs(long u,long c,long v)
{
ans = max(ans,v);
if (hash[num[u]+c])
{
long i = hash[num[u]+c];
if (v+(n-i+1) <= ans) return;
if (i>u && i<n+1 && num[i]-num[u] == c)
dfs(i,c,v+1);
}

}

long getint()
{
long rs=0;bool sgn=1;char tmp;
do tmp=getchar();
while (!isdigit(tmp)&&tmp-'-');
if (tmp=='-'){tmp=getchar();sgn=0;}
do rs=(rs<<3)+(rs<<1)+tmp-'0';
while (isdigit(tmp=getchar()));
return sgn?rs:-rs;
}

int main()
{
freopen("num.in","r",stdin);
freopen("num.out","w",stdout);

n = getint();
for (long i=1;i<n+1;i++)
{
num[i] = getint();
}
sort(num+1,num+1+n);
for (long i=1;i<n+1;i++)
{
hash[num[i]] = i;
}
for (long i=1;i<n+1;i++)
{
ans = max(ans,hash[num[i]]-i+1);
}

for (long i=1;i<n+1;i++)
{
if (num[i] == num[i-1]) continue;
for (long j=i+1;j<n+1;j++)
{
if (num[j] == num[j-1]) continue;
dfs(j,num[j]-num[i],2);
}
}

printf("%ld",ans);

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