【Codeforces】CF 8 C Looking for Order(状压dp)
题目
传送门:QWQ
分析
这种题不会做 吃枣药丸。。。。。
想到状压已经经过的点。
然后更新时枚举两个点加进去。
复杂度$ {O(2^n \times n^2)}$。
凉凉。
真正的做法是每一个状态只要找到一组解就break。这样可以省掉一层n。
大致上就像lrj紫书的dp例题一样,反正这个点都要选,那就先选了他。
还不是很懂这个神奇的break。。。。。
代码
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn=25, INF=1e9; 4 5 int dp[1<<maxn], dis[maxn][maxn], pre[1<<maxn]; 6 int x[maxn], y[maxn]; 7 8 int sqr(int x){ return x*x; } 9 10 int main(){ 11 scanf("%d%d",&x[0],&y[0]); 12 int n; scanf("%d",&n); 13 for(int i=1;i<=n;i++) { 14 scanf("%d%d",&x[i],&y[i]); 15 } 16 for(int i=0;i<=n;i++){ 17 for(int j=0;j<=n;j++){ 18 dis[i][j]=dis[j][i]=sqr(x[i]-x[j])+sqr(y[i]-y[j]); 19 } 20 } 21 22 memset(dp,127,sizeof(dp)); 23 24 dp[0]=0; 25 for(int S=0;S<(1<<n);S++){ 26 if(dp[S]>INF) continue; 27 for(int i=1;i<=n;i++){ 28 if(S&(1<<i-1)) continue; 29 for(int j=1;j<=n;j++){ 30 if(S&(1<<j-1)) continue; 31 32 int prenum=dp[S|1<<(i-1)|1<<(j-1)]; 33 dp[S|1<<(i-1)|1<<(j-1)]=min(dp[S|1<<(i-1)|1<<(j-1)], dp[S]+dis[0][i]+dis[0][j]+dis[i][j]); 34 if(prenum>dp[S|1<<(i-1)|1<<(j-1)]){ 35 pre[S|1<<(i-1)|1<<(j-1)]=S; 36 } 37 } 38 break; 39 } 40 } 41 42 printf("%d\n",dp[(1<<n)-1]); 43 int now=(1<<n)-1; //从所有都齐的状态开始逆推 44 while ( now!=0 ) 45 { 46 printf( "0 " ); 47 int update=now^pre[now]; 48 for ( int i=1; i<=n; i++ ) 49 if ( update&1<<i-1 ) 50 printf( "%d ", i ); //输出本次拿的哪些物品 51 now=pre[now]; 52 } 53 puts("0"); 54 }