P1433 吃奶酪

DFS方法

最后一组测试超时
时间复杂度O(n!)

#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
const int maxn = 20;

struct node {
	double x, y;
}f[maxn];
bool vis[maxn];
double dis[maxn][maxn], dis_min = 1e9;
int n;
void dfs(double sum, int cur, int step){
	if(sum >= dis_min){
		return;
	}
	if(step == n){
		if(dis_min > sum)
			dis_min = sum;
		return;
	}
	for(int i = 1; i <= n; i++){
		if(!vis[i]){
			vis[i] = true;
			dfs(sum + dis[ cur ][ i ], i, step + 1);
			vis[i] = false;
		}
	}
}
void Dis(){
	double d;
	for(int i = 1; i <= n; i++){
		d = sqrt(f[ i ].x*f[ i ].x + f[ i ].y*f[ i ].y);
		dis[i][0] = dis[0][i] = d;
	}
	for(int i = 1; i < n; i++){
		for(int j = i + 1; j <= n; j++){
			d = sqrt((f[ i ].x - f[ j ].x)*(f[ i ].x - f[ j ].x) + (f[ i ].y - f[ j ].y)*(f[ i ].y - f[ j ].y));
			dis[i][j] = d;
			dis[j][i] = d;
		}
	}
}
int main(){
	cin >> n;
	for(int i = 1; i <= n; i++)
		cin >> f[i].x >> f[i].y;
	Dis();
	dfs(0, 0, 0);	
	cout << setiosflags(ios::fixed) << setprecision(2) << dis_min << endl;	
	return 0;
} 

状压dp

时间复杂度O(n^2nn)
解题思路:
1、用二进制记录所有已走点位的状态(代码中使用的是K)

设 n = 3
状态有1, 2, 3,4, 5, 6, 7(二进制)
0	=>	0 0 0
1	=>	1 0 0
2	=>	0 1 0
3	=>	1 1 0
4	=>	0 0 1
5	=>	1 0 1
6	=>	0 1 1
7	=>	1 1 1
例:
3 则表示 1, 2两点已走过
7 则表示 1, 2, 3三点已走过

2、对状态k,找出两个没有走过的点 i,j。并以 i 为末尾,由dp[ j ][k - (1 << (i - 1))] 到 i ,得到dp[ i ][ k ]dp[ i ][ k ].
状态转移方程:
dp[i][k] = min(dp[i][k], dp[j][k - (1 << (i - 1))] + dis[i][j])

#include <bits/stdc++.h>
using namespace std;
const int maxn = 20;
double x[maxn], y[maxn];
int n;
double dis[maxn][maxn];
double dp[maxn][1 << 15 + 5], dis_min = 1e9;
void Dis_(){
	double d;
	for(int i = 0; i < n; ++i){
		for(int j = i + 1; j <= n; ++j){
			d = sqrt((x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]));
			dis[i][j] = dis[j][i] = d;
		}
	}
}
int main(){
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> x[i] >> y[i];
	}
	
	Dis_();
	
	for(int i = 1; i <= n; i++){
		for(int j = 1; j < (1 << n) + 9; j++){
			dp[i][j] = 1e9;
		}
	}
	for(int i = 1; i <= n; i++){
		dp[i][1 << (i - 1)] = dis[0][i]; 
	}
	
	for(int k = 1; k < (1 << n); ++k) {		//处理所有可能的点位状态
        for(int i = 1; i <= n; ++i) {		//下一个需要走到的点
            if((k & (1 << (i - 1))) == 0) continue;		//在k,若i已经走过,则不返回
            for(int j = 1; j <= n; ++j) {	
                if(i == j) continue;		//i j 不能相等
                if((k & (1 << (j - 1))) == 0) continue;	
                dp[i][k] = min(dp[i][k], dp[j][k - (1 << (i - 1))] + dis[i][j]);
            }
        }
    }
	for(int i = 1; i <= n; i++){
		dis_min = min(dis_min, dp[i][(1 << n)- 1]);
	}
	cout << setiosflags(ios::fixed) << setprecision(2);
	cout << dis_min << endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值