【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 }

 

posted @ 2018-08-23 23:50  noble_(noblex)  阅读(308)  评论(0编辑  收藏  举报
/* */