Codeforces 201D.Brand New Problem-状压dp
2018-02-23 18:27
573 查看
传送门
n<=15,m<=10,k<=500000
那么问题就转化成了把一个1到n的序列的全排列和一些序列进行匹配,找到能全匹配成功的最小的逆序对数
一个显然的状压dp状态是能想到的:f[i][j]f[i][j]表示前i位已经匹配了状态为j的数,最小的逆序对数,转移可以通过预处理达到O(n),但是这样一次的复杂度是O(k∗2n∗n)O(k∗2n∗n)的,显然复杂度爆炸,但是转移好像不能优化的样子….
所以我们需要换个状态!
我们发现k太大了,但是我们需要的只有n个数,也就是说原状态会有很多没有用的转移
所以我们尝试让状态与n相关
f[i][j]f[i][j]表示产生了i个逆序对,已经匹配了状态为j的数至少需要到文本串的第几位
然后我们再预处理一下辅助数组,就可以实现O(n)转移了
这样的话一次的复杂度是O(n2∗n∗2n)O(n2∗n∗2n),是非常优秀的
代码:
题意:
给出一个有n个字符串的匹配串,和m个有k个字符串的文本串,将匹配串进行全排列和文本串进行匹配,找到能全匹配成功的最小的逆序对数n<=15,m<=10,k<=500000
Solution:
通过哈希+map把字符串变成数字那么问题就转化成了把一个1到n的序列的全排列和一些序列进行匹配,找到能全匹配成功的最小的逆序对数
一个显然的状压dp状态是能想到的:f[i][j]f[i][j]表示前i位已经匹配了状态为j的数,最小的逆序对数,转移可以通过预处理达到O(n),但是这样一次的复杂度是O(k∗2n∗n)O(k∗2n∗n)的,显然复杂度爆炸,但是转移好像不能优化的样子….
所以我们需要换个状态!
我们发现k太大了,但是我们需要的只有n个数,也就是说原状态会有很多没有用的转移
所以我们尝试让状态与n相关
f[i][j]f[i][j]表示产生了i个逆序对,已经匹配了状态为j的数至少需要到文本串的第几位
然后我们再预处理一下辅助数组,就可以实现O(n)转移了
这样的话一次的复杂度是O(n2∗n∗2n)O(n2∗n∗2n),是非常优秀的
代码:
#include<cstdio> #include<iostream> #include<map> #include<cstring> using namespace std; const int bas=211; const int mod=1315326179; char s[11]; char c[500010][11]; int n,m,k,ha,len; int f[150][1<<15]; int num[20]; int go[500010][20];//i项以后最早和原序列第j项匹配的位置 map<int,int> vis; int ans=0,pos; int main() { scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%s",s+1); len=strlen(s+1); ha=0; for (int j=1;j<=len;j++) ha=(1ll*ha*bas+s[j])%mod; vis[ha]=i; } scanf("%d",&k); for (int t=1;t<=k;t++) { scanf("%d",&m); for (int i=1;i<=n;i++) go[m+1][i]=1e9; for (int i=1;i<=m;i++) scanf("%s",c[i]+1); for (int i=m;i>=1;i--) { for (int j=1;j<=n;j++) go[i][j]=go[i+1][j]; len=strlen(c[i]+1); ha=0; for (int j=1;j<=len;j++) ha=(1ll*ha*bas+c[i][j])%mod; int nw=vis[ha]; if (nw) go[i][nw]=i; } bool pan=0; for (int i=1;i<=n;i++) {go[0][i]=go[1][i];if (go[0][i]==1e9) {pan=1;break;}} if (pan) continue; memset(f,63,sizeof(f)); f[0][0]=0; for (int i=0;i<=n*(n-1)/2;i++) { if (i&&f[i-1][(1<<n)-1]<1e9) break; for (int j=0;j<(1<<n);j++) if (f[i][j]<1e9) { if (j==((1<<n)-1)) {if (ans<n*(n-1)/2-i+1) pos=t,ans=n*(n-1)/2-i+1;break;} for (int l=1;l<=n;l++) num[l]=num[l-1]+((j>>(l-1))&1); for (int p=1;p<=n;p++) if (!(1&(j>>(p-1)))) f[i+num -num[p]][j|(1<<p-1)]=min(f[i+num -num[p]][j|(1<<p-1)],go[f[i][j]][p]); } } } if (ans==0) printf("Brand new problem!"); else {printf("%d\n",pos);printf("[:");for (int i=1;i<=ans;i++) printf("|");printf(":]");} }
相关文章推荐
- [状压DP || 容斥 矩阵树定理] Codeforces 53E. Dead Ends
- CodeForces 895 C.Square Subsets(状压DP+线性基)
- Codeforces 201D Brand New Problem [状压dp]
- cf CodeForces - 165E 【状压dp】
- CodeForces - 482C Game with Strings(状压DP,期望)
- Codeforces 429C Guess the Tree(状压DP+贪心)
- Codeforces 16E Fish (状压dp+概率)
- codeforces 580D - Kefa and Dishes 状压dp - TSP
- CodeForces - 11D 【状压DP+无向图找环】
- CodeForces 21D Traveling Graph 状压dp+欧拉回路
- Codeforces 580D Kefa and Dishes【状压dp】
- Codeforces 401D Roman and Numbers【状压dp】
- CodeForces 599E Sandy and Nuts 状压DP
- Codeforces 31E TV Game 中途相遇法 状压dp
- Codeforces 678E Another Sith Tournament(状压dp,概率dp)
- [无向图简单环计数 状压DP] Codeforces 11D Beta Round #11 D. A Simple Task
- CodeForces 165E 【状压DP】
- codeforces 895C Square Subsets 线性基 或 状压DP
- Codeforces 453B Little Pony and Harmony Chest 状压DP
- codeforces 315B Sereja and Array(单点线段树)