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

送给圣诞夜的贺卡

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

降序排序,使得越到后面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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值