[CF544E]Remembering Strings_状压dp

E. Remembering Strings

题目大意

You have multiset of n strings of the same length, consisting of lowercase English letters. We will say that those strings are easy to remember if for each string there is some position i and some letter c of the English alphabet, such that this string is the only string in the multiset that has letter c in position i.

For example, a multiset of strings {"abc", "aba", "adc", "ada"} are not easy to remember. And multiset {"abc", "ada", "ssa"} is easy to remember because:

  • the first string is the only string that has character c in position 3;
  • the second string is the only string that has character d in position 2;
  • the third string is the only string that has character s in position 2.

You want to change your multiset a little so that it is easy to remember. For aij coins, you can change character in the j-th position of the i-th string into any other lowercase letter of the English alphabet. Find what is the minimum sum you should pay in order to make the multiset of strings easy to remember.

数据范围

The first line contains two integers n, m (1 ≤ n, m ≤ 20) — the number of strings in the multiset and the length of the strings respectively. Next n lines contain the strings of the multiset, consisting only of lowercase English letters, each string‘s length is m.

Next n lines contain m integers each, the i-th of them contains integers ai1, ai2, ..., aim (0 ≤ aij ≤ 106).



题解

被$Lijinnn$讲得贼神,看题解感觉还好。

首先我们需要发现,每一列的每一种字符只可能有两种改动情况。

第一种是把一行的一个字符改掉。

第二种是把这列中的某个字符全都改掉然后留下一个权值最大的。

这是显然的吧....不一定是唯一的但一定是最优的。

接下来就是$dp$了,假设$f[S]$表示状态为$S$的字符串合法了,考虑每列每行都有一种转移方式,暴力转移更新即可。

其实还有一个小优化,但是仗着$CF$评测机快没必要。

是这样的,就是我们发现,先更改某行某列,再更改另一行另一列,和把他们的顺序调换回来是一样的。

所以,我们在用$S$向后转移的时候,只需要任取一个$0$位往后转移即可。

不能保证中间的每一个$f[S]$都是最优的,但是能保证$f[(1<<n)-1]$是最优的,以及更新到最终答案的那一条链是最优的。

代码:(这个小优化就体现在,判断$0$的那个$if$中的那个$break$)

#include <bits/stdc++.h>

#define N 21 

using namespace std;

char s[N][N];

int w[N][N], sm[N][N], cs[N][N];

int dp[1 << N], n, m;

void dispose() {
    for (int i = 0; i < n; i ++ ) {
        for (int j = 0; j < m; j ++ ) {
            int al = 0, mx = 0;
            for (int k = 0; k < n; k ++ ) {
                if (s[i][j] == s[k][j]) {
                    al += w[k][j];
                    mx = max(mx, w[k][j]);
                    sm[i][j] |= (1 << k);
                }
            }
            cs[i][j] = al - mx;
        }
    }

    memset(dp, 0x3f, sizeof dp);

    dp[0] = 0;
    for (int s = 0; s < (1 << n); s ++ ) {
        for (int i = 0; i < n; i ++ ) {
            if(!(s >> i & 1)) {
                for (int j = 0; j < m; j ++ ) {
                    dp[s | (1 << i)] = min(dp[s | (1 << i)], dp[s] + w[i][j]);
                    dp[s | sm[i][j]] = min(dp[s | sm[i][j]], dp[s] + cs[i][j]);
                }
                break;
            }
        }
    }
}

int main() {
    cin >> n >> m ;
    for (int i = 0; i < n; i ++ ) {
        scanf("%s", s[i]);
    }
    for (int i = 0; i < n; i ++ ) {
        for (int j = 0; j < m; j ++ ) {
            scanf("%d", &w[i][j]);
        }
    }
    dispose();
    printf("%d\n", dp[(1 << n) - 1]);
    return 0;
}

小结:最后那个优化的思想其实是很好的,也是一个非常难想到的方法,期待掌握。

原文地址:https://www.cnblogs.com/ShuraK/p/11240627.html

时间: 2024-11-25 18:51:36

[CF544E]Remembering Strings_状压dp的相关文章

Codeforces 544E Remembering Strings 状压dp

题目链接 题意: 给定n个长度均为m的字符串 下面n行给出字符串 下面n*m的矩阵表示把对应的字母修改成其他字母的花费. 问: 对于一个字符串,若它是easy to remembering 当 它存在一个字母,使得这个字母在这一列是独一无二的. 要使得n个字符串都是easy to remembering 的最小花费. 第一个样例是把第一列的4个a中3个a修改成别的字母,所以花费为3. 思路: 显然是个状压dp,但需要一点转化. 首先得到一个结论: 对于某一列,设这一列的字母是 a,a,b,b,a

ZOJ3305Get Sauce 状压DP,

状压DP的题目留个纪念,首先题意一开始读错了,搞了好久,然后弄好了,觉得DFS可以,最后超时,修改了很久还是超时,没办法看了一下n的范围,然后觉得状压可以,但是没有直接推出来,就记忆化搜索了一下,可是一直错,莫名奇妙,然后没办法看了一下题解,发现了下面这个比较好的方法,然后按照这个方程去推,然后敲,也是WA了好多把,写的太搓了,没人家的清楚明了,唉~也算是给自己留个纪念,状压一直做的都不太好~唉~还好理解了, 参考了  http://blog.csdn.net/nash142857/articl

poj 2411 Mondriaan&#39;s Dream(状压DP)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12232   Accepted: 7142 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

(状压dp)uva 10817 Headmaster&#39;s Headache

题目地址 1 #include <bits/stdc++.h> 2 typedef long long ll; 3 using namespace std; 4 const int MAX=1e5+5; 5 const int INF=1e9; 6 int s,m,n; 7 int cost[125]; 8 //char sta[MAX]; 9 string sta; 10 int able[125]; 11 int dp[125][1<<8][1<<8]; 12 in

HDU5816 Hearthstone(状压DP)

题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5816 Description Hearthstone is an online collectible card game from Blizzard Entertainment. Strategies and luck are the most important factors in this game. When you suffer a desperate situation an

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

Travel(HDU 4284状压dp)

题意:给n个城市m条路的网图,pp在城市1有一定的钱,想游览这n个城市(包括1),到达一个城市要一定的花费,可以在城市工作赚钱,但前提有工作证(得到有一定的花费),没工作证不能在该城市工作,但可以走,一个城市只能工作一次,问pp是否能游览n个城市回到城市1. 分析:这个题想到杀怪(Survival(ZOJ 2297状压dp) 那个题,也是钱如果小于0就挂了,最后求剩余的最大钱数,先求出最短路和 Hie with the Pie(POJ 3311状压dp) 送披萨那个题相似. #include <

BZOJ 1087: [SCOI2005]互不侵犯King( 状压dp )

简单的状压dp... dp( x , h , s ) 表示当前第 x 行 , 用了 h 个 king , 当前行的状态为 s . 考虑转移 : dp( x , h , s ) = ∑ dp( x - 1 , h - cnt_1( s ) , s' ) ( s and s' 两行不冲突 , cnt_1( s ) 表示 s 状态用了多少个 king ) 我有各种预处理所以 code 的方程和这有点不一样 ------------------------------------------------

BZOJ 1072 排列 状压DP

题意:链接 方法:状压DP? 题解:这题其实没啥好写的,不算很难,推一推就能搞出来. 首先看到这个问题,对于被d整除这个条件,很容易就想到是取余数为0,所以想到可能状态中刚开始含有取余数. 先说我的第一个想法,f[i][j]表示选取i个数且此时的mod为j,这样的思想是第一下蹦出来的,当时想的就是在线来搞最终的答案.不过转瞬即发现,这TM不就是暴力吗魂淡!并没有什么卵用,于是开始想这个状态可不可以做什么优化. 显然第二维的j并不需要太大的优化,暂且先将其搁置一边,来考虑第一维的i怎么优化. 把滚