HDU 3001 Travelling (状压DP,3进制)

题意:

  给出n<=10个点,有m条边的无向图。问:可以从任意点出发,至多经过同一个点2次,遍历所有点的最小费用?

思路:

  本题就是要卡你的内存,由于至多可经过同一个点2次,所以只能用3进制来表示,3进制可以先将表打出来。在走的时候注意只能走2次,其他的和普通的TSP状压DP是一样的。注意点:重边,自环等等,老梗了。

 1 //#include <bits/stdc++.h>
 2 #include <iostream>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <cmath>
 6 #include <map>
 7 #include <algorithm>
 8 #include <vector>
 9 #include <iostream>
10 #define pii pair<int,int>
11 #define INF 0x3f3f3f3f
12 #define LL long long
13 #define ULL unsigned long long
14 using namespace std;
15 const double PI  = acos(-1.0);
16 const int N=11;
17 int g[N][N], dp[60000][N], up[N], bit[N];
18
19 int decode(int s)//将状态s解码
20 {
21     int cnt=0;
22     for(int i=1; s; i++)
23     {
24         bit[i]=s%3;
25         s/=3;
26         if(bit[i])  cnt++;
27     }
28     return cnt;
29 }
30
31 int cal(int n)
32 {
33     memset(dp,0x3f,sizeof(dp));
34     int ans=INF;
35     for(int i=1; i<=n; i++)    dp[up[i-1]][i]=0;//先将每个起点设为0,并不影响结果
36     for(int s=1; s<=up[n]; s++)
37     {
38         int cnt=decode(s);//cnt表示已经遍历的点数
39         for(int i=1; i<=n; i++)   //枚举中间点
40         {
41             if( bit[i]>0 )    //确保已经遍历过i点
42             {
43                 for(int j=1; j<=n; j++)           //枚举终点
44                 {
45                     if( bit[j]==2 )    continue;   //已经走过了2次
46                     bool flag=cnt==n?true:false;//为了更新答案
47                     if( bit[j]==0&&cnt+1==n )   flag=true;
48                     int &q=dp[s+up[j-1]][j];
49                     if( q>dp[s][i]+g[i][j] )    q=dp[s][i]+g[i][j];
50                     if(flag)                    ans=min(ans, q);
51                 }
52             }
53         }
54     }
55     return ans;
56 }
57
58 void init() //3进制先打表
59 {
60     int a=3;
61     up[0]=1;
62     for(int i=1; i<N; i++)
63     {
64         up[i]=a;
65         a*=3;
66     }
67 }
68
69
70 int main()
71 {
72     //freopen("input.txt","r",stdin);
73     init();
74     int n, m, a, b, c;
75     while(~scanf("%d%d",&n,&m)) //每个点至多可走2次
76     {
77         memset(g,0x3f,sizeof(g));
78         for(int i=1; i<=n; i++) g[i][i]=0;
79         for(int i=0; i<m; i++)
80         {
81             scanf("%d%d%d",&a,&b,&c);
82             g[a][b]=g[b][a]=min(g[a][b],c);
83         }
84         if(n==1)
85         {
86             puts("0");
87             continue;
88         }
89         int ans=cal(n);
90         printf("%d\n", ans==INF?-1:ans);
91     }
92     return 0;
93 }

AC代码

时间: 2024-10-29 10:46:07

HDU 3001 Travelling (状压DP,3进制)的相关文章

HDU 3001 Travelling 状压DP

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3001 题意:还是环游地图的问题,只不过这回旅行者对自己有着严格的要求,地图上每个点的经过次数不能超过两次. 思路:依然是状压DP问题,根上一道很像,只不过这次对于每个点来说有三种状态,分别是未经过,经过一次,经过两次.所以要用三进制的数来进行状态压缩,这个关键点想明白了其他的和上一道基本一样了.对于我来说需要注意的是:能够到达某一个点经过了两次的状态的前一个状态是这个点已经经过了一次的状态,而不是从来未

HDU 3001 Travelling (状压DP + BFS)

题意:有一个人要去旅游,他想要逛遍所有的城市,但是同一个城市又不想逛超过2次.现在给出城市之间的来往路费,他可以选择任意一个点为起点. 问逛遍所有城市的最低路费是多少. 析:用三进制表示每个城市的访问次数,然后 bfs 进行遍历,不过要注意这个题卡内存,必须要去年一些无用的状态,要不然会超内存的,还不能枚举每个城市, 这样可能会超时的,可以直接把所有的城市放进去,直接进行遍历.一个比较经典的题目. 代码如下: #pragma comment(linker, "/STACK:1024000000,

HDU 3001 Travelling 状态压缩dp+3进制

题意:一个人要旅行,他要去n个地方,且这n个地方每个地方最多可以走2次: 给m条路径,寻问最短花费 很明显的状态压缩,但是要求每个点最多只能走两次,就没办法标记当前点走过或是没走过,只能用三进制来表示 1代表地点1被走过一次. 2代表地点1被走过两次. 3(即10)代表地点2被走过一次. 4(即11)代表地点1被走过一次,地点2被走过一次. 5(即12)代表地点1被走过两次,地点2被走过一次. 附AC代码 #include<stdio.h> #include<string.h> i

UVA - 1412 状压dp九进制表示状态

此题难点在于用九进制表示状态,并且转移 这里九进制用vector表示,再用map作为此状态特有的标记 此处用刷表法,每一种状态转移都经历一遍,最终状态就是正确答案,注意界限 #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<map> #include<stack> #include<queue> #include&

HDU 4284Travel(状压DP)

HDU 4284    Travel 有N个城市,M条边和H个这个人(PP)必须要去的城市,在每个城市里他都必须要“打工”,打工需要花费Di,可以挣到Ci,每条边有一个花费,现在求PP可不可以从起点1走完所有的他必须要去的城市,打完所有的工,并且成功回到起点1 由于H<=15,所以显然可以状压,预处理这些都必须去的城市之间的最短距离(可以floyd),然后状压DP[S][i]表示他到达了S这些城市后正处在第i个城市里(所以S & (1<<i) != 0)的剩余的最大的钱的数量,然

HDU 4336 容斥原理 || 状压DP

状压DP :F(S)=Sum*F(S)+p(x1)*F(S^(1<<x1))+p(x2)*F(S^(1<<x2))...+1; F(S)表示取状态为S的牌的期望次数,Sum表示什么都不取得概率,p(x1)表示的是取x1的概率,最后要加一因为有又多拿了一次.整理一下就可以了. 1 #include <cstdio> 2 const int Maxn=23; 3 double F[1<<Maxn],p[Maxn]; 4 int n; 5 int main() 6

HDU 3681 BFS&amp;状压DP&amp;二分

N*M矩阵,从F点出发,走完所有的Y点,每走一格花费1点电量,走到G点时,电量充满,D不可到达,问起始时的最小满电量可以走完所有Y,Y和G一共最多15个 先BFS出所有的F,Y,G之间的最短距离. 然后二分起始电量,对每个电量,做状压DP判断是否可行 #include "stdio.h" #include "string.h" #include "queue" using namespace std; int inf=0x3f3f3f3f; in

LianLianKan HDU - 4272(状压dp)

题意:就是连连看,有两个相同的就能消除,再加上两个特别的规定,一是只能从栈顶开始消除,而是两个相同的元素之间距离不能超过6,询问能否消除序列中所有元素. 思路:数据水,贪心就能过,但严谨的考虑,贪心显然不能解决所有问题.这题虽然序列很长,但是状态并不复杂,可以使用滚动的状压dp,然后考虑使用多少位表示状态,每一种状态表示第i位为栈首的序列状态,该位前最多能消除掉该位后四位,所以我们只需要表示后9位的状态即可,总计10位. 某位上1表示该位已被消除,0表示未被消除. dp[i][j]表示从i位开始

方格取数(1) HDU - 1565 (状压dp)

给你一个n*n的格子的棋盘,每个格子里面有一个非负数.     从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大. Input包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20) Output对于每个测试实例,输出可能取得的最大的和 Sample Input 3 75 15 21 75 15 28 34 70 5 Sample Output 188 思路:状压dp,建立dp[i][j]二维数组,表示第i行状

hdu 2825 (Aho-Corasick &amp; 状压DP) - xgtao -

题目链接 给出m(m<=10)个模板串,问长度为n(n<=25)的字符串包含至少k个模板串的种类数,对20090717取模. 套路,有多个模板串考虑Aho-Corasick,因为想了很久找不到什么可行办法做,就考虑Dp,因为m<=10,所以可以有状压来检查当前状态下已经包含多少个模板串了,因为在一棵trie树上,那么定义状态也好定义,dp[len][id][s]表示长度为len时处在第id个节点并且状态为s,转移方程为dp[len+1][nextid][s|nexts] += dp[le