#8 C. Looking for Order (状压dp + 路径记忆)

题目链接:点击打开链接

http://codeforces.com/contest/8/problem/C

题意:
給定原点 ( sx, sy ),以及 N 個坐标 X, Y。每次至多选两个坐标,依序拜访完后,回到原点。问你最好的路径,使得总路径最小。兩個坐标的路径长为欧几里得距离的平方。

题解:
看N(N<=24)这么小,肯定可以状压dp。2^24。
状压dp。
dp[ i ] : 已完成 i 的拜访,此时的最小总花费。
如果用枚举的方法来更新DP,肯定会超时的。
关键在于:拜访是没有顺序的。然后在每一次拿东西的时候,都需要更新出两个状态,一种是只拿一个,另一种是拿两个。
由于与拜访的顺序无关,每个点又必须被使用,所以每次转移的時候,第一個选取的坐标就固定为还沒有拜访的座标里下标最小的。这么一來转移就只需要 O( N )。

dp时要记录一下路径就可以了。

复杂度:O(n*2^n)。

AC代码:
//#pragma comment(linker, "/STACK:102400000,102400000")
//#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <map>
#include <cmath>
#include <queue>
#include <set>
#include <bitset>
#include <iomanip>
#include <list>
#include <stack>
#include <utility> 
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;
const double eps = 1e-8;  
const int INF = 1e9+7; 
const ll inf =(1LL<<62) ;
const int MOD = 1e9 + 7;  
const ll mod = (1LL<<32);
const int N =1e6+6; 
const int M=100010;
const int maxn=1001;
#define mst(a) memset(a, 0, sizeof(a))
#define M_P(x,y) make_pair(x,y)
#define pi acos(-1.0)
#define in freopen("in.txt","r",stdin) 
#define rep(i,j,k) for (int i = j; i <= k; i++)  
#define per(i,j,k) for (int i = j; i >= k; i--)  
#define lson x << 1, l, mid  
#define rson x << 1 | 1, mid + 1, r  
const int lowbit(int x) { return x&-x; }
int read(){ int v = 0, f = 1;char c =getchar();
while( c < 48 || 57 < c ){if(c=='-') f = -1;c = getchar();}
while(48 <= c && c <= 57) v = v*10+c-48, c = getchar();
return v*f;}
int dp[1<<24];
int pre[1<<24];
int dis[30][30],x[30],y[30];
int n;
int dfs1(int cur)//n*2^n 
{
	if(dp[cur]!=-1)return dp[cur];
	dp[cur]=1e9;
	for(int i=0;i<n;i++)
	{
		if(cur&(1<<i))
		{
		//	cout<<"i="<<(cur^(1<<i))<<endl;
			int nxt = dfs1 (cur^(1<<i));
			if(dp[cur] > nxt + dis[n][i] + dis[i][n] )
			{
				dp[cur] = nxt + dis[n][i] + dis[i][n];
				//printf("dp[%d]=%d\n",cur,dp[cur]);
				pre[cur] = cur^(1<<i);
			}
			for(int j=i+1;j<n;j++)
			{
				if(cur&(1<<j))
				{			
						//cout<<"j="<<(cur^(1<<j))<<endl;
					//	cout<<"ij="<<(cur^(1<<i)^(1<<j))<<endl;
						nxt = dfs1(cur^(1<<i)^(1<<j));
						if(dp[cur] > nxt + dis[n][i] + dis[i][j] + dis[j][n])
						{
							dp[cur] = nxt + dis[n][i] + dis[i][j] + dis[j][n];
						//	printf("dp[%d]=%d\n",cur,dp[cur]);
							pre[cur] = cur^(1<<i)^(1<<j);
 						}
				}

			}
			break;// 剪枝,不剪会TLE
		}
	}
	return dp[cur];
}
int dfs2(int cur)
{
	if(cur)
	{
		for(int i=0;i<n;i++)
		{
			//if((cur^cur^(1<<i))||cur^cur^(1<<i)&&(1<<i))
			if((cur^pre[cur])&(1<<i))	
			{
				cout<<i+1<<" ";
			}
		}
		cout<<"0 ";//回到原点 
		dfs2(pre[cur]);
	}
}
int main()
{
	cin>>x[0]>>y[0];
	cin>>n;
	x[n]=x[0];y[n]=y[0];
	for(int i=0;i<n;i++)
	{
		cin>>x[i]>>y[i];
	}
	for(int i=0;i<=n;i++)
	{
		for(int j=0;j<=n;j++)
		{
			dis[i][j]=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
		}
	}
	memset(dp,-1,sizeof(dp));
	dp[0]=0;
	cout<<dfs1((1<<n)-1)<<endl;
//	cout<<dp[(1<<n)-1]<<endl; //一样的 
	cout<<"0 ";
	dfs2((1<<n)-1);
	cout<<endl;
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值