codeforces round# 302 (div1 C) (状压dp)

本题目的意思是给定n(n<=20)行长度都为过m(m<=20)的字符串,并且给定一个n*m的数字矩阵,cost(i,j)为将第i行第j个字母修改成任意字母的代价。

求用最小的代价让所有的字符串,都存在一个位置,在同列中没有和它相同的字符。

考虑d[ s ]代表状态s下(其中1代表该位置已经唯一,0反之)还需的最小代价,

转移只有两种,找到最小的没被唯一化得位置p, 枚举修改位置j, 可以 (1)修改j本身   或者  ( 2 )  将j列里所有与该位置第j列有相同字符的修改费用加起来减去一个最大值。

因为左右的可行性转移只有这两种;

还有一个问题 , 比如 n = 4, m = 4,   其中 字符 s[ 1 ][ 3 ] = s[ 2 ][ 3 ] = s[ 3 ][ 3 ];  那么如果直接修改1,3将第一行唯一化,到后面,决策3时采用了(2 ),那么导致唯一化1的费用可能被计算两边,这个问题不需要担心,因为有比他更优且不产生矛盾的解存在,且同过这种转移可以计算到。

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <string>
#include <list>
#include <cstdlib>
#include <queue>
#include <stack>
#include <cmath>
#include <bitset>
#include <cassert>
#define ALL(a) a.begin(), a.end()
#define clr(a, x) memset(a, x, sizeof a)
#define fst first
#define snd second
#define pb push_back
#define lowbit(x) (x&(-x))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define rep1(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) for(int i=0;i<(int)n;i++)
using namespace std;
const double eps = 1e-10;
typedef long long LL;
typedef long long ll;
typedef pair<int, int> pii;
const int oo =0x3f3f3f3f;

const int N = 25;
char s[N][N];
int cost[N][N],sum[N][N],mask[N][N];
int n,m;
int d[1<<20];
int main()
{
   scanf("%d %d",&n,&m);
   rep(i,n) scanf("%s",s[i]);
   rep(i,n) rep(j,m) scanf("%d",&cost[i][j]);
   rep(i,n) rep(j,m){
      int max_ = 0;
      sum[i][j] = mask[i][j] = 0;
      rep(k,n)if(s[i][j] == s[k][j]){
          sum[i][j]+=cost[k][j];
          mask[i][j]|=(1<<k);
          max_=max(max_,cost[k][j]);
      }
      sum[i][j]-=max_;
   }
   int lim = (1<<n)-1;
   d[lim] = 0;
   for(int s=lim-1;s>=0;s--){
      d[s] = oo;
      int p = 0;
      while(p <n-1 && s&(1<<p)) p++;
      for(int i=0;i<m;i++){
         d[s] = min(d[s] , d[s|(1<<p)]+cost[p][i]);
         d[s] = min(d[s] , d[s|mask[p][i]]+sum[p][i]);
      }
   }
   cout<<d[0]<<endl;
   return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值