洛谷1433吃奶酪(状压dp)

  给出n个点(n<=15),从(0,0)出发,问最短走多远,才能经过所有的点,可以在任何位置结束。

  今天有学弟来问的一个题,之前做的时候还没学状压,写了个爆搜+剪枝,数据太水$O(n!)$给水过去了,今天再看发现算是个状压dp的入门题吧,之前的代码提交发现数据加强TLE了,就又补了一下。

  对经过的点状态进行状压,状态表示为i,位运算上每位为0表示未经过,为1则表示经过,采用dp[i][j]表示i状态下,若当前在j点的最短距离,对于已经经过的点k,那么可以从k走到j点,就有$dp[i][j]=dp[i-(1<<j)][k]+dis(j,k)$,转移方程也就出来了,$dp[i][j]=min(dp[i][j],dp[i-(1<<j)][k]+dis(k,j)$,其中满足$ (1<<k)&i $不为0,转移也就很方便了,最后对满状态下的(1<<n)-1,枚举最后一项取最小的即可,时间复杂度$O(n*2^{n})$。

  最后附上代码

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair <double,double> pdd;
#define rep(i,x,y) for(int i=x;i<y;i++)
#define rept(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)
#define all(x) x.begin(),x.end()
#define pb push_back
#define fi first
#define se second
#define mes(a,b) memset(a,b,sizeof a)
#define mp make_pair
#define dd(x) cout<<#x<<"="<<x<<" "
#define de(x) cout<<#x<<"="<<x<<"\n"
#define debug() cout<<"I love Miyamizu Mitsuha forever.\n"
const double inf=1e18;
pdd point[20];
double dp[35000][20];

double dis(const pdd &s1,const pdd &s2)
{
    return sqrt( (s1.fi-s2.fi)*(s1.fi-s2.fi)+(s1.se-s2.se)*(s1.se-s2.se) );
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin>>n;
    rep(i,0,n) cin>>point[i].fi>>point[i].se;
    rep(i,1,1<<n)
    {
        rep(j,0,n)
        {
            dp[i][j]=inf;
            if((1<<j)&i)
            {
                if((1<<j)==i)
                {
                    dp[i][j]=dis(mp(0,0),point[j]);
                    continue;
                }
                else
                {
                    rep(k,0,n)
                        if(k!=j&&((1<<k)&i))
                            dp[i][j]=min(dp[i][j],dp[i-(1<<j)][k]+dis(point[k],point[j]));
                }
            }
        }
    }
    double ans=inf;
    rep(i,0,n)
        ans=min(ans,dp[(1<<n)-1][i]);
    cout<<fixed<<setprecision(2)<<ans<<"\n";
    return 0;
}

 

posted @ 2020-02-17 20:42  GGMU  阅读(227)  评论(0编辑  收藏  举报