前言
  • 最近学了基于连通性的状压DP,也就是插头DP,写了几道题,发现这DP实质上就是状压+分类讨论,轮廓线什么的也特别的神奇。下面这题把我WA到死…

HDU-5531 Efficient Tree
  • 给出一个n∗mn*mn∗m的网格图,以及相邻四联通格子之间的边权。

    对于命题xxx,如果xxx成立,那么 [x]=1[x]=1[x]=1,否则 [x]=0[x]=0[x]=0

    对于一颗生成树,每个点的贡献为 1+[有一条连向上的边]+[有一条连向左的边]1+[有一条连向上的边]+[有一条连向左的边]1+[有一条连向上的边]+[有一条连向左的边]。这棵树的贡献为所有点的贡献之积。要求:

    • 最小生成树的边权和
    • 所有最小生成树的贡献之和。

    n&lt;=800,m&lt;=7n&lt;=800, m&lt;=7n<=800,m<=7

  • 由于mmm最大只有777,考虑轮廓线DP:

    • f(i,j,S)f(i,j,S)f(i,j,S) 表示当前在 (i,j)(i,j)(i,j)这个点,轮廓线上方的格子联通情况为SSS的边权之和
    • g(i,j,S)g(i,j,S)g(i,j,S) 表示当前在 (i,j)(i,j)(i,j)这个点,轮廓线上方的格子联通情况为SSS的贡献之和

    • 黄色格子就是当前考虑的点,蓝色部分就是上一次状压的 mmm 个格子
    • 此处的SSS是用最小表示法状压的,状态是长度为 mmm 的 888 进制数,每个位置上的值表示每一个蓝色格子在哪一个联通块里
    • 这里的状压与普通插头DP有点不同,普通插头DP的状态长度是m+1m+1m+1,因为多算上了中间那一条竖着的轮廓线,而这里并不用。
  • 转移比较简单,只要注意两点:

    • 如果当前格子上面的格子在单独一个联通块,那么这条向上的边必须选,否则就无法形成一棵联通的树。
    • 如果上面的格子和左边的格子在同一个联通块就不能合并,否则构成了一个环。
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 805, MAXM = 10;
const int HASH = 10007, NUM = 1000005;
const int mod = 1e9+7, inf = 0x3f3f3f3f;
struct node {
int w, sum;
node(int _w=0, int _sum=0):w(_w), sum(_sum){}
inline node operator +(const node &t)const {
if(w < t.w) return *this;
if(w > t.w) return t;
return node(w, (sum+t.sum)%mod);
}
inline node operator +(const int &t)const {
return node(w+t, sum);
}
inline node operator *(const int &t)const {
return node(w, 1ll*sum*t%mod);
}
};
int n, m;
int L[MAXN][MAXM], U[MAXN][MAXM];
struct HashMap {
int val[NUM], adj[HASH], pre[NUM], tot;
node f[NUM];
inline void init() {
memset(adj, -1, sizeof adj); tot = 0;
}
inline void insert(const int &x, const node &tmp) {
int u = x % HASH;
for(int i = adj[u]; ~i; i = pre[i])
if(val[i] == x) {
f[i] = f[i] + tmp; return;
}
val[++tot] = x; pre[tot] = adj[u]; adj[u] = tot; f[tot] = tmp;
}
}h[2];
int msk[MAXM], id[MAXM];
inline void decode(int x) {
for(int i = m; i; --i, x>>=3) msk[i] = x&7;
}
inline int encode(const int &m) {
int res = 0, cur = 0;
memset(id, -1, sizeof id);
id[0] = 0;
for(int i = 1; i <= m; ++i) {
if(id[msk[i]] == -1) id[msk[i]] = ++cur;
res = (res<<3)|id[msk[i]];
//注意这里不能 msk[i]=id[msk[i]], 否则改变了 msk[i]的值, dp函数中的x,y值就不对了
}
return res;
}
inline void dp(const int &i, const int &j, const int &now) {
for(int k = 1; k <= h[now^1].tot; ++k) {
decode(h[now^1].val[k]);
int x = msk[j-1], y = msk[j];
bool one = 1;
for(int l = 0; l <= m; ++l) //看是否是独立的联通块
if(l != j && msk[l] == y) { one = 0; break; }
if(!one) { //自成联通块
msk[j] = 8; //赋值为不可能出现的8
h[now].insert(encode(m), h[now^1].f[k]);
msk[j] = y;
}
if(i > 1)
h[now].insert(encode(m), (h[now^1].f[k] + U[i][j])*2);//只连左边
if(j > 1 && !one) {
msk[j] = x;
h[now].insert(encode(m), (h[now^1].f[k] + L[i][j])*2);//只连上边
msk[j] = y;
}
if(i > 1 && j > 1 && x != y) { //两边都连
for(int l = 0; l <= m; ++l) if(msk[l] == x) msk[l] = y;
h[now].insert(encode(m), (h[now^1].f[k] + L[i][j] + U[i][j])*3);
}
}
}
int main () {
int T;
scanf("%d", &T);
for(int kase = 1; kase <= T; ++kase) {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
for(int j = 2; j <= m; ++j)
scanf("%d", &L[i][j]);
for(int i = 2; i <= n; ++i)
for(int j = 1; j <= m; ++j)
scanf("%d", &U[i][j]);
int now = 0; h[now].init(); h[now].insert(0, node(0, 1));
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j) {
now ^= 1; h[now].init();
dp(i, j, now);
}
node ans = node(inf, 0);
for(int i = 1; i <= h[now].tot; ++i) {
bool flag = 1;
decode(h[now].val[i]);
for(int j = 1; j < m; ++j)
if(msk[j] != msk[j+1]) { flag = 0; break; }
if(flag) ans = ans + h[now].f[i];
}
printf("Case #%d: %d %d\n", kase, ans.w, ans.sum);
}
}

PS:PS:PS:

  • 我之所以WA就是因为encodeencodeencode函数里改变了mskmskmsk的值,出来后dpdpdp函数里的x,yx,yx,y就不对了,WA到自闭…

  • 还有我的代码只用了msk[1...m]msk[1...m]msk[1...m],第000位没用

HDU - 5513 Efficient Tree(轮廓线DP)的更多相关文章

  1. HDU 5513 Efficient Tree

    HDU 5513 Efficient Tree 题意 给一个\(N \times M(N \le 800, M \le 7)\)矩形. 已知每个点\((i-1, j)\)和\((i,j-1)\)连边的 ...

  2. hdu 5534 Partial Tree 背包DP

    Partial Tree Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid= ...

  3. HDU - 4804 Campus Design 轮廓线dp

    题意:一个nm的矩阵被12的骨牌和11的骨牌完全覆盖,11的骨牌只能放c-d次,矩阵中有障碍物 题解:dp[i][j][k]表示到了第i行,第j个状态,放过k个11的骨牌,当前位有障碍物时只有一种转移 ...

  4. HDU 1565 方格取数(1) 轮廓线dp

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1565 方格取数(1) Time Limit: 10000/5000 MS (Java/Others) ...

  5. HDU 4802 && HDU 4803 贪心,高精 && HDU 4804 轮廓线dp && HDU 4805 计算几何 && HDU 4811 (13南京区域赛现场赛 题目重演A,B,C,D,J)

    A.GPA(HDU4802): 给你一些字符串对应的权重,求加权平均,如果是N,P不计入统计 GPA Time Limit: 2000/1000 MS (Java/Others)    Memory ...

  6. HDU - 4804 Campus Design(状压+轮廓线dp)

    Campus Design Nanjing University of Science and Technology is celebrating its 60th anniversary. In o ...

  7. 2013 ACM-ICPC亚洲区域赛南京站C题 题解 轮廓线DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4804 题目大意 给你一个 \(n \times m\) 的矩形区域.你需要用 \(1 \times 1 ...

  8. 轮廓线DP POJ3254 && BZOJ 1087

    补了一发轮廓线DP,发现完全没有必要从右往左设置状态,自然一点: 5 6 7 8 9 1 2 3 4 如此设置轮廓线标号,转移的时候直接把当前j位改成0或者1就行了.注意多记录些信息对简化代码是很有帮 ...

  9. HDU 1003 Max Sum --- 经典DP

    HDU 1003    相关链接   HDU 1231题解 题目大意:给定序列个数n及n个数,求该序列的最大连续子序列的和,要求输出最大连续子序列的和以及子序列的首位位置 解题思路:经典DP,可以定义 ...

随机推荐

  1. Django框架(十二)-- 中间件、CSRF跨站请求伪造

    中间件 一.什么是中间件 请求的时候需要先经过中间件才能到达django后端(urls,views,templates,models) 响应的时候也需要经过中间件才能到达web服务网关接口 djang ...

  2. redis源码分析(二)-rio(读写抽象层)

    Redis io抽象层 Redis中涉及到多种io,如socket与file,为了统一对它们的操作,redis设计了一个抽象层,即rio,使用rio可以实现将数据写入到不同的底层io,但是接口相同.r ...

  3. Redis缓存雪崩、击穿、穿透

    参考大佬 前言 Redis在互联网技术存储方面使用如此广泛,几乎所有的后端技术面试官都要在Redis的使用和原理方面对小伙伴们进行360°的刁难.作为一个在互联网公司面一次拿一次offer的面霸(请允 ...

  4. oracle学习笔记(三)

    索引: drop table test1 purge; drop table test2 purge; drop table test3 purge; drop table t purge; crea ...

  5. Spring-Cloud之Ribbon负载均衡-3

    一.负载均衡是指将负载分摊到多个执行单元上,常见的负载均衡有两种方式.一种是独立进程单元,通过负载均衡策略,将请求转发到不同的执行单元上,例如 Ngnix .另一种是将负载均衡逻辑以代码的形式封装到服 ...

  6. C语言--线性表

    #include<stdio.h>#include<stdlib.h>#include<string.h>#define LIST_SIZE 100#define ...

  7. txt文件每行内容与图片文件名字组合,输出txt格式

    import os dir_list = os.listdir('C:\\Users\\10107472\\Desktop\\practice\\JPEGImages')i=0f1=open('C:\ ...

  8. Privoxy搭建代理服务器

    Privoxy搭建代理服务器 Docker Hub镜像地址 Dockerfile FROM alpine EXPOSE 8118 RUN apk --no-cache --update add pri ...

  9. Matlab装饰模式

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.根据https://www.runoob.com/design-pattern/decorator ...

  10. INTERVAL 用法 mysql

    原文:https://blog.csdn.net/sqlquan/article/details/82699237 做个例子描述吧,也许更易于理解. 准备: 1.建表 create table INT ...