hdu 3920 Clear All of Them I 状态压缩 动态规划

本文详细解析了 HDU 3920 的解题思路,对比了正向 DP 和记忆化搜索两种方法,并通过具体代码展示了如何优化算法以达到题目要求的时间限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

hdoj 3920 http://acm.hdu.edu.cn/showproblem.php?pid=3920

 

 

   刚拿这道题同学告诉我能费用流 然后我看了他建图 然后我就推翻了他的图......

   看到此题数据n==10  20个点 瞬间想到2^20==100万左右 然后就想到了位压缩动态规划

 

   状态转移方程dp[i]=min(dp[i-(1<<a)-(1<<b)]+cost(a and b));  (i中二进制1(从右往左)表示消灭了该点敌人 0表示还没消灭)

   不过做了一下午 这题始终没能A  一直time limit exceed

   超时的不只我一个人 这题限时是1000MS 我想说的是你多开一两秒钟不行吗 好多人都超时啊你知道不?

   我优化又优化都是超时啊 输入外挂都用上了还是返回绿色的字啊 其实也怪我对状态这个概念理解不深

   位压缩更是只接触过2道小题

   

   不说了 这个题根据状态转移方程 我最开始的做法是顺推从状态0到状态(1<<(n<<1))-1

   经过潜入研究 要取两个点最优 必然是先取其中一个到起点最近的点 然后再取较远的点

   所以我预先把所有点按照离起点距离从小到大排序

   然后没遍历到一个状态i  就从低位到高位找1 找到第一个1记录位置 然后枚举另外一个1

   dp[i]=min(dp[i-(1<<a)-(1<<b)]+dist(st,node[a])+dist(node[a],node[b])); (a表示低位1的位置 b有很多个 表示第二个1的位置

dist()是平面2点距离)

 

    因为我事先队坐标排过序 这样转移一定是最优解了

    而且我都预处理了很多不合法状态(1有奇数个 1<2个的状态都不合法)

    最终我的时间复杂度最坏情况是O(50万*k) k<20

    结果就是超时

    我就纳闷了 我以前DP从来都是顺推的 怎么这次超时了 主要是测试cases太多了 100组

    然后找到了原因 比如状态 1111 可以从 1100 和0011转移过来

    其实只要求其中任意一个转移的耗费就行了 因为先去前2个1和后2个1对最终状态没影响

    说白了就是当前这个状态不受前面取点的顺序影响

    因为状态转移方程每次都取了最低为那个1 然后再枚举另一个1  就是枚举哪个1和这个组合

    正推会遍历很多无效状态

    后面倒推就避免了这个情况了 倒推的状态我现在还是有点没理解好

   

    倒推说白了就是记忆化搜索 这个刘汝佳也专门提到过 动态规划的3种经典方向

    记忆化搜索 否则还是 超时  总之我对这题很无语 多开一两秒又怎么了嘛 非要卡我正推的时间

      400个提交就15个人过了 估计很多人也有和我相同的感受吧!

 

    我先把我超时的正推代码放下 结果是正确的 只是超时

    我个人还是不怎么喜欢深搜的 比较费栈

    正常情况正推的DP都会快些的 不耗栈也无需判重

   

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;
#define MAXN 1111111
#define INF 999999999

struct Point
{
	double x,y;
}st,node[22];

double dp[MAXN];
int bit[MAXN];

inline double dist(const Point &a,const Point &b)
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

bool cmp(const Point a,const Point b)
{
	return dist(st,a)<dist(st,b);
}

inline double min(double x,double y)
{
	return x<y?x:y;
}

int countbit(int v)
{
	int r=0;
	for(;v;v&=v-1)
		r++;
	return r;
}

int main()
{
	int t,casenum;
	int i,j,n,top;

	top=1<<20;
	for(i=3,n=0;i<top;i++)
	{
		j=countbit(i);
		if((j&1) || j<2)
			continue;
		bit[n++]=i;
	}
	bit[n]=INF;
	printf("%d\n",n);
	//for(i=0;i<n;i++)
	//{
	//	printf("%d\n",bit[i]);
	//}
	scanf("%d",&t);
	for(casenum=1;casenum<=t;casenum++)
	{
		scanf("%lf %lf",&st.x,&st.y);
		scanf("%d",&n);
		n<<=1;
		for(i=1;i<=n;i++)
		{
			scanf("%lf %lf",&node[i].x,&node[i].y);
		}

		sort(node+1,node+1+n,cmp);
		memset(dp,0,sizeof(dp));

		top=1<<n;
		int a,b,c;

		for(i=0;bit[i]<top;i++)
		{
			int temp=bit[i];
			for(a=-1,c=1;a==-1;temp>>=1,c++)
			{
				if(temp & 1)
					a=c;
			}
			dp[bit[i]]=INF;
			for(;temp;temp>>=1,c++)
			{
				if(temp & 1)
				{
					b=c;
					dp[bit[i]]=min(dp[bit[i]],dp[bit[i]-(1<<(a-1))-(1<<(b-1))]
						+dist(st,node[a])+dist(node[a],node[b]));
				}
			}
		}
		printf("Case #%d: ",casenum);
		printf("%.2lf\n",dp[top-1]);
	}
	return 0;
}


 

 

然后是Ac代码:

   

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;
#define MAXN 1111111
#define INF 0x3fffffff

struct Point
{
	double x,y;
}node[22];
int n;

double hash[MAXN];
double dist(Point a,Point b)
{
	return sqrt((a.x-b.x)*(a.x-b.x)
		+(a.y-b.y)*(a.y-b.y));
}

bool nodecmp(const Point a,const Point b)
{
	return dist(node[0],a)<dist(node[0],b);
}

double min(double x,double y)
{
	return x<y?x:y;
}

double dfs(int sta,int cnum)
{
	if(hash[sta]>=0)
		return hash[sta];
	if(cnum==n)
		return 0;

	int pos=-1;
	int temp=sta;
	hash[sta]=INF;
	for(int c=1;temp;temp>>=1,c++)
	{
		if(temp & 1)
		{
			if(pos==-1)
				pos=c;
			else
				hash[sta]=min(hash[sta],dfs(sta-(1<<(pos-1))-(1<<(c-1)),cnum+1)
				+dist(node[0],node[pos])+dist(node[pos],node[c]));
		}
	}
	return hash[sta];
}

int main()
{
	int t,casenum;
	int i,upper;

	scanf("%d",&t);
	for(casenum=1;casenum<=t;casenum++)
	{
		scanf("%lf %lf",&node[0].x,&node[0].y);
		scanf("%d",&n);

		upper=n<<1;

		for(i=1;i<=upper;i++)
		{
			scanf("%lf %lf",&node[i].x,&node[i].y);
		}

		sort(node+1,node+1+upper,nodecmp);
		memset(hash,-1,sizeof(hash));

		double ans=dfs((1<<upper)-1,0);
		printf("Case #%d: %.2lf\n",casenum,ans);
	}
	return 0;
}


有疑问可以留言 我会尽量解答的 顺便我也加深理解...

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值