您的位置:首页 > 其它

Codeforces 201D Brand New Problem [状压dp]

2018-03-10 12:16 232 查看
Description:

给出一个有n个字符串的匹配串,和m个有k个字符串的文本串,将匹配串进行全排列和文本串进行匹配,找到能全匹配成功的最小的逆序对数。

Solution:

比较显然的dpdp是dp[i][j]dp[i][j]状态为ii,匹配到jj的最小逆序对数,然而这样复杂度过高。考虑常用技巧更换状态,将逆序对计入状态。dp[i][j]dp[i][j]表示状态为ii,逆序对数为jj最短匹配距离,距离最短自然方案更优。转移则考虑下一个字符选什么,预处理每个位置下一个字符的位置即可。

#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
const int N = 5e5 + 5;
map<string, int> mp;
int n, _, ret = -1, pp, m;
int dp[1 << 16][125], po[16], nxt
[16], a
;
char s[105];
int main() {
scanf("%d", &n);
for(int i = 0; i < n; ++i) {
scanf("%s", s);
mp[s] = i;
}
pp = n * (n - 1) / 2 + 1;
scanf("%d", &_);
for(int id = 1; id <= _; ++id) {
scanf("%d", &m);
memset(po, 0x3f3f, sizeof(po));
for(int i = 1; i <= m; ++i) {
scanf("%s", s);
a[i] = mp.count(s) ? mp[s] : -1;
}
for(int i = m; ~i; --i) {
for(int j = 0; j < n; ++j) {
nxt[i][j] = po[j];
}
if(a[i] != -1) {
po[a[i]] = i;
}
}
memset(dp, 0x3f3f, sizeof(dp));
dp[0][0] = 0;
for(int i = 0; i < 1 << n; ++i) {
for(int j = 0; j <= n * (n - 1) / 2; ++j) {
for(int k = 0; k < n; ++k) {
if(!(i & (1 << k)) && dp[i][j] <= m) {
int &p = dp[i ^ (1 << k)][j + __builtin_popcount(i >> k)];
p = min(p, nxt[dp[i][j]][k]);
}
}
}
}
for(int i = pp - 1; ~i; --i) {
if(dp[(1 << n) - 1][i] <= m) {
ret = id;
pp = i;
}
}
}
if(ret == -1) {
puts("Brand new problem!");
} else {
printf("%d\n[:", ret);
for(int i = 1; i <= n * (n - 1) / 2 + 1 - pp; ++i) {
printf("|");
}
puts(":]");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: