P1433 吃奶酪-状压dp

P1433 吃奶酪-状压dp

这是5.15晚自习的题目预习

直接上题逝一逝吧

放心,是对的代码

P1433 吃奶酪 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

记录详情 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

我真喜欢记忆化搜索(嘿嘿嘿)

记忆化搜索的话,更容易理解

dp[x] [zt]设定为走到点x时,状态为zt

而显然zt之和(“选/没选”)两种情况有关,所以设置一个二进制数,i-1位为1说明i选过了,为0则说明没选择

先考虑下最终状态,由于不知道最后是以哪个点结尾的,所以要用n的时间来枚举最后一个点是什么,而zt嘛,肯定是最后所有的都被选择了的,所以就是11111111….(n-1个1),而为了写的简便一些就是(1<<n)-1

考虑下转移,首先他肯定是从某个吃完了的地方(假如说是i,那么就是i-1=1的地方)转移过来,毕竟我从这个点i来都来了,这个点i上肯定吃了最好,而转移过来的状态我设置为zt1,显然就是把当前位置(x-1)的位置设为0(因为状态设置的是在x点时的状态,在x点时这个点肯定是被吃过的,为1,那么从他转移过来的点i肯定还没有吃x点上的,那么两者的状态就缺了个‘0’),这个可以用减法实现

最后想想末位状态,初始化即可

代码如下

#include<bits/stdc++.h>
using namespace std;
struct node{
    double x;
    double y;
}a[20];
double dis(double x,double y,double xx,double yy){
    return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
int n;
double dp[20][1<<16];
double dfs(int x,int zt){//在x点时的状态
    if(dp[x][zt]!=999999){
        return dp[x][zt];
    }
    int zt1=zt-(1<<(x-1));
    for(int i=1;i<=n;i++){//枚举转移到zt的点,且状态位置为1的点
        if((zt1>>(i-1))&1==1){//这一位上是1
            dp[x][zt]=min(dp[x][zt],dfs(i,zt1)+dis(a[x].x,a[x].y,a[i].x,a[i].y));
        }
    }
    return dp[x][zt];
}
int main(){
    ios::sync_with_stdio(false);
    cin >> n;
    a[0].x=0;
    a[0].y=0;
    for(int i=1;i<=n;i++){
        cin >> a[i].x>>a[i].y;
    }
    //cout<<(1<<16);
    for(int i=0;i<=n;i++){
    	for(int j=0;j<=(1<<n);j++){
    		dp[i][j]=999999;
    		//cout<<dp[i][j]<<endl;
		}
	}
	//cout<<dp[1][15];
    for(int i=1;i<=n;i++){
        dp[i][1<<(i-1)]=dis(a[0].x,a[0].y,a[i].x,a[i].y);
    }
	
	double ans=9999999;
	for(int i=1;i<=n;i++){
		ans=min(ans,dfs(i,(1<<(n))-1));
	}
	printf("%.2f",ans);
}


posted @ 2023-05-15 22:43  铃狐sama  阅读(29)  评论(0)    收藏  举报