HDU 1964 Pipes (插头DP,变形)

题意:给一个n*m的矩阵,每个格子都是必走的,且无障碍格子,每对格子之间都有一个花费,问哈密顿回路的最小花费。

思路:

  这个和Formula1差不多,只是求得是最小花费,这只需要修改一下DP值为花费就行了,主要是在创建新括号,以及延续一个插头的时候花费,因为可能上一个格子有多个状态都可以转移到本格子的同一状态,那么同一个格子中的同一状态的DP值取最小值即可。

  1 #include <bits/stdc++.h>
  2 #include <iostream>
  3 #include <cstdio>
  4 #include <cstring>
  5 #define pii pair<int,int>
  6 #define INF 0x3f3f3f3f
  7 #define LL long long
  8 using namespace std;
  9 const int N=105;
 10
 11 int g[N][N], cur, n, m;
 12 struct Hash_Map
 13 {
 14     static const int mod=12357;
 15     static const int NN=100010;
 16     int head[mod];      //桶指针
 17     int next[NN];        //记录链的信息
 18     LL  status[NN];      //状态
 19     LL  value[NN];       //状态对应的DP值。
 20     int size;
 21
 22     void clear()    //清除哈希表中的状态
 23     {
 24         memset(head, -1, sizeof(head));
 25         size = 0;
 26     }
 27
 28     void insert(LL st, LL val)  //插入状态st的值为val
 29     {
 30         int h = st%mod;
 31         for(int i=head[h]; ~i; i=next[i])
 32             if(status[i] == st) //这个状态已经存在,累加进去。
 33             {
 34                 value[i] = min(value[i], val);
 35                 return ;
 36             }
 37         status[size]= st;           //找不到状态st,则插入st。
 38         value[size] = val;
 39         next[size] = head[h] ;      //新插入的元素在队头
 40         head[h] = size++;
 41     }
 42 }hashmap[2];
 43
 44 inline int getbit(LL s,int pos)   //取出状态s的第pos个插头
 45 {
 46     return (s>>2*pos)&3;
 47 }
 48 inline int setbit(LL s,int pos,int bit)   //将状态s的第pos个插头设置为bit
 49 {
 50     if(s&(1<<2*pos ))     s^=1<<(2*pos);
 51     if(s&(1<<(2*pos+1)))  s^=1<<(2*pos+1);
 52     return (s|(bit<<2*pos));
 53 }
 54
 55 int Fr(LL s,int pos,int bit)   //寻找状态s的第pos个插头对应的右括号。
 56 {
 57     int cnt=0;
 58     for(pos+=2; pos<m; pos++)
 59     {
 60         if(getbit(s, pos)==3-bit)   cnt++;
 61         if(getbit(s, pos)==bit)     cnt--;
 62         if(cnt==-1)         return setbit(s, pos, 3-bit);
 63     }
 64 }
 65 int Fl(LL s,int pos,int bit)   //寻找状态s的第pos个插头对应的左括号。
 66 {
 67     int cnt=0;
 68     for(pos--; pos>=0; pos--)
 69     {
 70         if(getbit(s, pos)==3-bit)  cnt++;
 71         if(getbit(s, pos)==bit)    cnt--;
 72         if( cnt==-1)    return setbit(s, pos, 3-bit);
 73     }
 74 }
 75
 76 void DP(int i,int j)
 77 {
 78     LL t;
 79     for(int k=0; k<hashmap[cur^1].size; k++)
 80     {
 81         LL s=hashmap[cur^1].status[k];
 82         LL v=hashmap[cur^1].value[k];
 83         int R=getbit(s, j), D=getbit(s, j+1);
 84         if(R && D)  //两个括号
 85         {
 86             t=(setbit(s,j,0)&setbit(s,j+1,0));
 87             if(R==D)    //同个方向的括号
 88             {
 89                 if(R==1)    t=Fr(t, j, 2);  //要改
 90                 else        t=Fl(t, j, 1);
 91                 hashmap[cur].insert(t, v);
 92             }
 93             else if( R==2 && D==1 )        //不同的连通分量
 94                 hashmap[cur].insert(t, v);
 95             else if( i+1==n && j+1==m && t==0 )        //终点才能闭合
 96                 hashmap[cur].insert(t, v);
 97         }
 98         else if(R || D)     //仅1个括号
 99         {
100             if(R)   //右插头
101             {
102                 if(i+1<n )   hashmap[cur].insert(s, v+g[i*m+j+1][i*m+m+j+1]);//往下
103                 if(j+1<m )   hashmap[cur].insert(setbit(setbit(s,j,0), j+1, R), v+g[i*m+j+1][i*m+j+2]);//往右
104             }
105             else    //下插头
106             {
107                 if(j+1<m )   hashmap[cur].insert(s, v+g[i*m+j+1][i*m+j+2]); //往右
108                 if(i+1<n )   hashmap[cur].insert(setbit(setbit(s,j+1,0), j, D), v+g[i*m+j+1][i*m+m+j+1]);//往下
109             }
110         }
111         else                //无括号
112         {
113             if(  j+1<m && i+1<n )    //新括号
114             {
115                 v+=g[i*m+j+1][i*m+m+j+1];   //下
116                 v+=g[i*m+j+1][i*m+j+2];     //右
117                 hashmap[cur].insert( setbit(s,j,1)|setbit(s,j+1,2), v);
118             }
119         }
120     }
121 }
122
123 void cal()
124 {
125     for(int i=0; i<n; i++)
126     {
127         cur^=1;
128         hashmap[cur].clear();
129         for(int j=0; j<hashmap[cur^1].size; j++)    //新行,需要左移一下状态。
130             hashmap[cur].insert( hashmap[cur^1].status[j]<<2, hashmap[cur^1].value[j] );
131         for(int j=0; j<m; j++)
132         {
133             cur^=1;
134             hashmap[cur].clear();
135             DP(i,j);
136         }
137     }
138 }
139 LL print()
140 {
141     for(int i=0; i<hashmap[cur].size; i++)
142         if(hashmap[cur].status[i]==0)
143             return hashmap[cur].value[i];
144     return 0;
145 }
146
147
148 int main()
149 {
150     //freopen("input.txt", "r", stdin);
151     int t, Case=0;
152     cin>>t;
153     while(t--)
154     {
155         memset(g,0,sizeof(g));
156         n=m=cur=0;
157         scanf("%d%d",&n,&m);
158         for(int i=1; i<n; i++)      //输入
159         {
160             for(int j=1; j<m; j++)  //左右
161             {
162                 char c=getchar();
163                 if(isdigit(c))
164                     g[i*m-m+j][i*m-m+j+1]=g[i*m-m+j+1][i*m-m+j]=c-‘0‘;
165                 else j--;
166             }
167             for(int j=1; j<=m; j++) //上下
168             {
169                 char c=getchar();
170                 if(isdigit(c))
171                     g[i*m-m+j][i*m+j]=g[i*m+j][i*m-m+j]=c-‘0‘;
172                 else j--;
173             }
174         }
175         for(int j=1; j<m; j++)      //最后一行:左右
176         {
177             char c=getchar();
178             if(isdigit(c))
179                 g[n*m-m+j][n*m-m+j+1]=g[n*m-m+j+1][n*m-m+j]=c-‘0‘;
180             else j--;
181         }
182         char s[20];
183         scanf("%s%s",s,s);//多余
184
185         hashmap[cur].clear();
186         hashmap[cur].insert(0, 0);  //初始状态:花费0
187         cal();
188         cout<<print()<<endl;
189     }
190     return 0;
191 }

AC代码

时间: 2024-10-01 03:47:01

HDU 1964 Pipes (插头DP,变形)的相关文章

【插头DP】HDU 1964 Pipes

通道:http://acm.hdu.edu.cn/showproblem.php?pid=1964 题意:单回路,权值,无阻碍. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 7 const int MAX_N = 13; 8 const int MAX_M = 13; 9 const int HASH = 10007; 10

插头DP专题

建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议先理解“插头”的概念.然后会HASH表(这个其实是很基础的东西,应该都会的).然后就是DP. 以及特殊题目的特殊处理. 好像一般是求N,M<=12的网格图的某种回路数或某种通路数的方案数. 大体上每个题说几句特殊处理,有问题请纠正....题目的顺序基本上难度递增 另外代码我都是用括号匹配的.因为感觉连通

HDU 3377 Plan (插头DP,变形)

题意:有一个n*m的矩阵,每个格子中有一个值(可能负值),要从左上角走到右下角,求路径的最大花费. 思路: 除了起点和终点外,其他的点可以走,也可以不走. (2)我用的是括号表示法,所以起始状态为')',即仅有一个右括号,那么到右下角也应该是只有一个右括号.因为,如果碰到()),加粗表示起点的那个右括号,那么合并后变成)##,仍然是右括号,如果是)(),那么合并后变成##),仍然是右括号,相当于延续了.插头每到达一个格子就先将其值给加上,如果要合并的时候,再减掉(因为多算了一次),因此,新括号的

HDU 4804 Campus Design(插头DP)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4804 题意:给定一个图,0是不能放的,然后现在有1X1和1X2方块,最后铺满该图,使得1X1使用次数在C到D之间,1X2次数随便,问有几种放法 思路:插头DP的变形,只要多考虑1X1的情况即可,然后DP多开一维表示使用1X1的个数 代码: #include <stdio.h> #include <string.h> #include <algorithm> #include

HDU 4113 Construct the Great Wall(插头dp)

好久没做插头dp的样子,一开始以为这题是插头,状压,插头,状压,插头,状压,插头,状压,无限对又错. 昨天看到的这题. 百度之后发现没有人发题解,hust也没,hdu也没discuss...在acm-icpc信息站发现难得的一篇题解.不过看到是插头二字之后,代码由于风格太不一样就没看了,自己想了好久,想通了.然后就等到今天才码.... 如果把点看成网格,那就可以实现,没有公共点公共边等限定条件,也显然是插头dp的最短单回路的模型.这是本题的一个难点(当时想到这样是因为,题目要求计算最短周长,显然

HDU 1693 Eat the Trees 插头DP

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1693 题意:给出一块r*c的地,(r,c<=11),其中有的土地上种上了树,有些没有种上树,只能在种上树的地上走,通过走若干个回路,来走遍所有种树的土地.问有多少种走法. 思路:无论如何还是要先提供一个链接:http://wenku.baidu.com/view/4fe4ac659b6648d7c1c74633.html,是国家队论文,里面对于要提及的概念讲解的十分清楚. dp的过程是从左上角的点到右下

hdu 1693 Eat the Trees (插头dp入门)

Eat the Trees Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2507    Accepted Submission(s): 1225 Problem Description Most of us know that in the game called DotA(Defense of the Ancient), Pudg

hdu 4804 Campus Design(插头dp)

题目链接:hdu 4804 Campus Design 题目大意:有1?2的木块无穷个,要求在给定的图n?m的图上,用1?2和1?1的木块铺满,图上的0表示不需要铺的位置,1表示必须要铺的位置.并且1?1的使用数量必须在c到d之间.求总方案数. 解题思路:和uva11270一样的做法,只是需要多添加一位状态来表示用掉1得个数,以及要对当前位置判断是否为可放. #include <cstdio> #include <cstring> #include <algorithm>

HDU 4949 Light(插头dp、位运算)

比赛的时候没看题,赛后看题觉得比赛看到应该可以敲的,敲了之后发现还真就会卡题.... 因为写完之后,无限TLE... 直到后来用位运算代替了我插头dp常用的decode.encode.shift三个函数以及改改HASH值才勉强过的...7703ms 题意:给一个N*M的01矩阵,每次可以选一个格子进行2种操作,①翻转邻居格子②翻转邻居格子和自己.输出最小的总操作数使得矩阵全为0. 显然每个格子有4种操作(一.不操作:二.①②:三.①:四.②). 一开始写的时候用2位表示一个插头,一位用于表示翻转