luogu P1433

传送门

题目描述

房间里放着 \(n\) 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 \((0,0)\) 点处。

输入格式

第一行一个正整数 \(n\)

接下来每行 \(2\) 个实数,表示第i块奶酪的坐标。

两点之间的距离公式为 \(\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}\)

输出格式

一个数,表示要跑的最少距离,保留 \(2\) 位小数。

说明/提示

\(1 \leq n \leq 15\)

题解

第一眼:暴力dfs一下不就行了吗?为啥是普及+/提高啊?
然后写了10多分钟,写完了

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <bitset>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <sstream>
#include <stack>
#include <iomanip>
#include <assert.h>
using namespace std;
#define pb push_back
#define mp make_pair
typedef pair<int,int> pii;
typedef long long ll;
typedef double ld;
typedef vector<int> vi;
#define fi first
#define se second
#define fe first
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
#define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
#define es(x,e) (int e=fst[x];e;e=nxt[e])
#define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
typedef pair<ld,ld> pdd;
int n;
pdd a[20];
ld ans=114514114514.000;
bool u[20];
ld dis(pdd x,pdd y)
{return sqrt((x.fi-y.fi)*(x.fi-y.fi)+(x.se-y.se)*(x.se-y.se));}
void dfs(int lst,int x,ld sum)
{
    if(x==n)
    {
        ans=min(ans,sum);
        return;
    }
    if(sum>ans) return;
    for(int i=1;i<=n;++i) if(!u[i])
    {
        u[i]=1;
        dfs(i,x+1,sum+dis(a[lst],a[i]));
        u[i]=0;
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;++i) cin>>a[i].fi>>a[i].se;
    dfs(0,0,0.0);
    cout.setf(ios::fixed);
    cout.precision(2);
    cout<<ans<<"\n";    
}

交上去后


然后卡了一年常数,还是没过
然后我突然发现,这不是个状压dp吗?

然后设计了下状态:dp[s][i]表示当前状态为s(二进制下第\(i-1\)位代表第\(i\)个数有没有选),上一个选的数是\(i\)的最小值。注:二进制的最低位是第0位
然后转移方程就立马出来了~
\[ dp[s][i] = \min_{1 \leq j \leq n}{dp[s \quad xor \quad (1<<(i-1))][j]+dis(i,j)} \]
也就是说\(dp[s][i]\)可以从任何一个之前没选过\(i\),并且只选\(i\)就变成\(s\)的状态转移过来
喜闻乐见的代码

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef double ld;
typedef pair<ld,ld> pdd;
pdd a[20];
int n;
ld dp[1<<15][20],ans=9999999999999999.000;
ld dis(int x,int y)
{return sqrt((a[x].fi-a[y].fi)*(a[x].fi-a[y].fi)+(a[x].se-a[y].se)*(a[x].se-a[y].se));} //编号为i,j两点间的距离
int main()
{
    memset(dp,127,sizeof(dp)); 
    cin>>n;
    for(int i=1;i<=n;++i) cin>>a[i].fi>>a[i].se;
    for(int i=1;i<=n;++i) dp[1<<(i-1)][i]=dis(i,0); //初始化,从(0,0)点开始走到某一个点
    for(int s=1;s<1<<n;++s)
    {
        for(int i=1;i<=n;++i) if(s&(1<<(i-1))) //只有s当中有i时才转移
        {
            for(int j=1;j<=n;++j) dp[s][i]=min(dp[s][i],dp[s^(1<<(i-1))][j]+dis(i,j)); //转移方程
        }
    }
    for(int i=1;i<=n;++i) ans=min(ans,dp[(1<<n)-1][i]); //统计一下答案
    cout.setf(ios::fixed);
    cout.precision(2);
    cout<<ans<<"\n";
}

跑的飞快

猜你喜欢

转载自www.cnblogs.com/Saudi/p/12416968.html
今日推荐