HDU 4848 Wow! Such Conquering! 深搜+强剪枝

题意:n个点,告诉两两点之间的距离,然后求从1开始到其它所有的点的距离的和。

每一个点有一个时间底线,只有不超过时间底线才有可能到达此点否则无法到达。

输出距离和的最小值。


想法:因为有两两点之间的距离,所以可以枚举这个人到达所有点的顺序。有两个剪枝:1.如果当前时刻要到达k点,那么k点的时间底线一定要大于等于当前时刻+map[now][k]。

     2.让总时间先走一步。因为时间是累加的,类似于:

        1

        1+2

        1+2+3

        1+2+3+4

        ............

        1+2+3+4+5+...+n

        显然可以发现1被加了n次,2被加了n-1次,以此类推k被加了n-(k-1)次,所以在计算总时间的时候可以采用这种方法,让总时间先行一步,如果此时的总时间大于已有的总时间答案的话,那么写下来的数都不用再去枚举了。

#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 0x7ffffff
using namespace std;
int n,ans;
int map[35][35],dl[35],maxdl;
int vis[35]; 
void floyd()
{
	for(int k=1;k<=n;k++)
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(map[i][j]>map[i][k]+map[k][j])
				{
					map[i][j]=map[i][k]+map[k][j];
				}
			}
		}
	}
}
void dfs(int pos,int hasnum,int tim,int kk,int sumtim)
{
	if(tim>maxdl) return;
	if(tim>dl[pos]) return;
	if(sumtim>ans) return;//很重要 
	for(int i=2;i<=n;i++)
	{
		if(!vis[i]&&tim+map[pos][i]>dl[i]) return;
	}
	if(hasnum==n)
	{
		if(sumtim<ans) ans=sumtim;
		return;
	}
	for(int i=2;i<=n;i++)
	{
		if(vis[i]) continue;
		vis[i]=1;
		dfs(i,hasnum+1,tim+map[pos][i],kk-1,sumtim+map[pos][i]*kk);//sumtim预处理了很多组数据 
		vis[i]=0;
	}
}
int main()
{
	while(~scanf("%d",&n))
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				scanf("%d",&map[i][j]);
			}
		}
		maxdl=-1;
		for(int i=2;i<=n;i++)
		{
			scanf("%d",&dl[i]);
			if(maxdl<dl[i]) maxdl=dl[i];
		}
		floyd();
		ans=inf;
		memset(vis,0,sizeof(vis));
		dfs(1,1,0,n-1,0);
		if(ans==inf) printf("-1\n");
		else printf("%d\n",ans);
	}
	return 0;
} 

你可能感兴趣的:(HDU 4848 Wow! Such Conquering! 深搜+强剪枝)