洛谷 3959 宝藏——枚举+状压dp
题目:https://www.luogu.org/problemnew/show/P3959
原来写了个不枚举起点的状压dp。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=15,M=(1<<12)+5,INF=0x3f3f3f3f; int n,m,lm,b[N][N],dis[M][N]; ll dp[M]; int main() { scanf("%d%d",&n,&m);lm=(1<<n); memset(b,0x3f,sizeof b); int x,y,z; while(m--) { scanf("%d%d%d",&x,&y,&z); b[x][y]=min(b[x][y],z);b[y][x]=b[x][y]; } memset(dp,0x3f,sizeof dp);dp[0]=0; for(int i=1;i<=n;i++)dp[1<<(i-1)]=0,dis[1<<(i-1)][i]=1; for(int s=1;s<lm;s++) for(int i=1;i<=n;i++) if((s&(1<<(i-1)))==0) { int d=(s|(1<<(i-1))); for(int j=1;j<=n;j++) if(s&(1<<(j-1))) if(dp[s]+(ll)b[i][j]*dis[s][j]<dp[d])//b可能是0x3f3f3f { dp[d]=dp[s]+(ll)b[i][j]*dis[s][j]; memcpy(dis[d],dis[s],sizeof dis[s]); dis[d][i]=dis[s][j]+1; } } printf("%lld\n",dp[lm-1]); return 0; }
但其错误是不能改dis。没有最优子结构的性质。
然后看了题解,发现如果枚举起点,就行了。
其实和原来想的差不多,但原来的不同起点的dis最后会混到一起,进而少遍历一些状态。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=15,M=(1<<12)+5,INF=0x3f3f3f3f; int n,m,lm,b[N][N],dis[N]; ll dp[M],ans=INF; void dfs(int S) { for(int i=1;i<=n;i++) if((S&(1<<(i-1)))==0) { int D=(S|(1<<(i-1))); for(int j=1;j<=n;j++) if(S&(1<<(j-1))&&b[i][j]<INF) if(dp[S]+b[i][j]*dis[j]<dp[D]) { dp[D]=dp[S]+b[i][j]*dis[j]; dis[i]=dis[j]+1; dfs(D); dis[i]=0; } } } int main() { scanf("%d%d",&n,&m);lm=(1<<n); memset(b,0x3f,sizeof b); int x,y,z; while(m--) { scanf("%d%d%d",&x,&y,&z); b[x][y]=min(b[x][y],z);b[y][x]=b[x][y]; } for(int i=1;i<=n;i++) { memset(dp,0x3f,sizeof dp);dp[1<<(i-1)]=0; memset(dis,0x3f,sizeof dis);dis[i]=1; dfs(1<<(i-1)); ans=min(ans,dp[lm-1]); } printf("%lld\n",ans); return 0; }