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

本文介绍了一道CodeForces C题目的解题思路及实现细节。通过状态压缩动态规划解决最优路径问题,实现了O(n*2^n)的时间复杂度。

题目链接:点击打开链接

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;
}


六、DP的优化技巧 6.1 预处理合法态 很多问题中,大部分态是不合法的,可以预先筛选: cpp vector valid_states; for (int state = 0; state < (1 << n); ++state) { if (check(state)) { // 检查state是否合法 valid_states.push_back(state); } } 6.2 滚动数组优化 当态只依赖前一个阶段时,可以节省空间: cpp vector<vector> dp(2, vector(size)); // 只保留当前和上一个态 int now = 0, prev = 1; for (int i = 1; i <= n; ++i) { swap(now, prev); for (auto& state : valid_states) { dp[now][state] = 0; // 清空当前态 // 态转移… } } 6.3 记忆化搜索实现 有时递归形式更直观: cpp int memo[1<<20][20]; // 记忆化数组 int dfs(int state, int u) { if (memo[state][u] != -1) return memo[state][u]; // 递归处理… return memo[state][u] = res; } 七、常见问题与调试技巧 7.1 常见错误 位运算优先级:总是加括号,如(state & (1 << i)) 数组越界:态数是2ⁿ,不是n 初始态设置错误:比如TSP中dp[1][0] = 0 边界条件处理不当:如全选态是(1<<n)-1,不是1<<n 7.2 调试建议 打印中间态:将二进制态转换为可视化的形式 cpp void printState(int state, int n) { for (int i = n-1; i >= 0; --i) cout << ((state >> i) & 1); cout << endl; } 从小规模测试用例开始(如n=3,4) 使用assert检查关键假设 八、学习路线建议 初级阶段: 练习基本位操作 解决简单问题(如LeetCode 464、526题) 中级阶段: 掌握经典模型(TSP、棋盘覆盖) 学习优化技巧(预处理、滚动数组) 高级阶段: 处理高维(如需要同时缩多个态) 结合其他算法(如BFS、双指针) 九、实战练习题目推荐 入门题: LeetCode 78. Subsets(理解态表示) LeetCode 464. Can I Win(简单DP) 中等题: LeetCode 526. Beautiful Arrangement LeetCode 691. Stickers to Spell Word 经典题: POJ 2411. Mondriaan’s Dream(棋盘覆盖) HDU 3001. Travelling(三进制) 挑战题: Codeforces 8C. Looking for Order Topcoder SRM 556 Div1 1000. LeftRightDigitsGame2 记住,掌握DP的关键在于: 彻底理解二进制态表示 熟练运用位运算 通过大量练习培养直觉 希望这份超详细的教程能帮助你彻底掌握DP!如果还有任何不明白的地方,可以针对具体问题继续深入探讨。 请帮我转成markdown语法输出,谢谢
最新发布
08-13
#8 688.7 + cd /tmp/build/opentelemetry-cpp-contrib-8933841f0a7f8737f61404cf0a64acf6b079c8a5/instrumentation/nginx #8 688.7 + mkdir -p build #8 688.7 + cd build #8 688.7 + cmake -DCMAKE_BUILD_TYPE=Release -G Ninja -DCMAKE_CXX_STANDARD=17 -DCMAKE_INSTALL_PREFIX=/tmp/otel -DBUILD_SHARED_LIBS=ON -DNGINX_VERSION=1.25.5 .. #8 688.9 -- The C compiler identification is GNU 8.5.0 #8 689.0 -- The CXX compiler identification is GNU 8.5.0 #8 689.1 -- Detecting C compiler ABI info #8 689.2 -- Detecting C compiler ABI info - done #8 689.3 -- Check for working C compiler: /usr/bin/cc - skipped #8 689.3 -- Detecting C compile features #8 689.3 -- Detecting C compile features - done #8 689.3 -- Detecting CXX compiler ABI info #8 689.5 -- Detecting CXX compiler ABI info - done #8 689.5 -- Check for working CXX compiler: /usr/bin/c++ - skipped #8 689.5 -- Detecting CXX compile features #8 689.5 -- Detecting CXX compile features - done #8 689.5 -- Performing Test CMAKE_HAVE_LIBC_PTHREAD #8 689.7 -- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed #8 689.7 -- Looking for pthread_create in pthreads #8 689.7 -- Looking for pthread_create in pthreads - not found #8 689.7 -- Looking for pthread_create in pthread #8 689.9 -- Looking for pthread_create in pthread - found #8 689.9 -- Found Threads: TRUE #8 689.9 CMake Error at /usr/share/cmake/Modules/CMakeFindDependencyMacro.cmake:76 (find_package): #8 689.9 Found package configuration file: #8 689.9 #8 689.9 /usr/local/lib/cmake/grpc/gRPCConfig.cmake #8 689.9 #8 689.9 but it set gRPC_FOUND to FALSE so package "gRPC" is considered to be NOT #8 689.9 FOUND. Reason given by package: #8 689.9 #8 689.9 The following imported targets are referenced, but are missing: #8 689.9 protobuf::libprotobuf protobuf::libprotoc #8 689.9 #8 689.9 Call Stack (most recent call first): #8 689.9 /usr/lib64/cmake/opentelemetry-cpp/opentelemetry-cpp-config.cmake:105 (find_dependency) #8 689.9 CMakeLists.txt:5 (find_package) #8 689.9 #8 689.9 #8 689.9 -- Configuring incomplete, errors occurred! #8 ERROR: process "/bin/sh -c /build.sh" did not complete successfully: exit code: 1
06-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值