题目描述:
C. Connect
Time Limit: 5000ms
Memory Limit: 65536KB
64-bit integer IO format: %lld Java class name: Main
Submit Status PID: 39565
Connect
You are playing a solitaire puzzle called “Connect”, which uses several letter tiles.
There are R × C empty cells. For each i (1 ≤ i ≤ R), you must put a string si (1 ≤ |si| ≤ C) in the i-th row of the table, without changing the letter order. In other words, you choose an integer sequence {aj} such that 1 ≤ a1 < a2 < … < a|si| ≤ C , and put the j-th character of the string si in the aj-th column (1 ≤ j ≤ |si|).
For example, when C = 8 and si = “ICPC”, you can put si like followings.
I_C_P_C_
ICPC____
_IC___PC
‘_’ represents an empty cell.
For each non-empty cell x, you get a point equal to the number of adjacent cells which have the same character as x. Two cells are adjacent if they share an edge.
Calculate the maximum total point you can get.
Input
The first line contains two integers R and C (1 ≤ R ≤ 128, 1 ≤ C ≤ 16).
Then R lines follow, each of which contains si (1 ≤ |si| ≤ C). All characters of si are uppercase letters.
Output
Output the maximum total point in a line.
Sample Input 1
2 4
ACM
ICPC
Output for the Sample Input 1
2
Sample Input 2
2 9
PROBLEMF
CONNECT
Output for the Sample Input 2
6
Sample Input 3
4 16
INTERNATIONAL
COLLEGIATE
PROGRAMMING
CONTEST
Output for the Sample Input 3
18
题解:
其实是一个很简单的状压dp:
有一个很重要的特性,就是每个字符串必须全部放进去,并且顺序不能够改变,那么一横行其实用CXX就可以很快表示,但是如果一行一行的枚举,复杂度还是会超,我们模仿放格子,状压一层,发现下面那层可以从左边推出来是第几个,上面那层可以从后面推出来是第几个.这样就完全搞定了.
重点:
格子状压两种方法,放格子的方法转压一层的小技巧.
其实放格子不只可以状压一层,还记得今年网络赛的坑..
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)
typedef long long ll;
using namespace std;
const int MAX_STA = (1<<16)+100;
const int tot = (1<<16);
const int MAX_N = 128 + 10;
const int MAX_M = 20;
int f[MAX_M][MAX_STA];
char str[MAX_N][MAX_M];
int slen[MAX_N];
int xiao[20];
int num[MAX_STA];
int n, m;
int change(int sta, int x, int y)
{
int ans = 0;
int nowsta = (xiao[y]&sta);
int laststa = sta - nowsta;
int nownum = num[nowsta];
if(nownum>=slen[x])
return -1;
int lastnum = slen[x-1] - num[laststa];
if(((1<<y)&sta) && str[x][nownum] == str[x-1][lastnum])
{
ans++;
}
if(y!=0)
{
if(((1<<(y-1))&sta) && str[x][nownum-1] == str[x][nownum])
ans++;
}
return ans;
}
void pre()
{
for(int i = 0; i<tot; i++)
{
num[i] = 0;
for(int j = 0; j<16; j++)
{
if((1<<j)&i)
num[i]++;
}
}
xiao[0] = 0;
for(int i = 1; i<16; i++)
{
xiao[i] = (xiao[i-1]|(1<<(i-1)));
}
}
void solve()
{
int key = (1<<m);
memset(f, -1, sizeof(f));
f[0][0] = 0;
for(int i = 1; i<=n; i++)
{
for(int j = 0; j<m; j++)
{
for(int s = 0; s<key; s++)
{
if(f[j][s]==-1)
continue;
int nxty = j+1;
int tmp = f[j][s];
int nxtS = (s&(~(1<<j)));
f[nxty][nxtS] = max(f[nxty][nxtS], tmp);
int add = change(s, i, j);
if(add!=-1)
{
nxtS = (s|((1<<j)));
f[nxty][nxtS] = max(f[nxty][nxtS], tmp+add);
}
}
}
for(int j = 0;j<m;j++)
{
for(int s = 0;s<key;s++)
f[j][s] = -1;
}
for(int s = 0; s<key; s++)
{
if(f[m][s]!=-1)
{
if(num[s]==slen[i])
f[0][s] = f[m][s];
}
}
for(int s = 0;s<key;s++)
f[m][s] = -1;
// if(i == 3)
// {
// for(int j = 0; j<key; j++)
// {
// if(f[i][0][j]!=-1)
// {
// printf("s is %d %d\n", j, f[i][0][j]);
// }
// }
// }
}
int ans = 0;
for(int s = 0; s<key; s++)
{
ans = max(ans, f[0][s]);
}
printf("%d\n", ans*2);
}
int main()
{
// freopen("3Cin.txt", "r", stdin);
//freopen("3Cout.txt", "w", stdout);
pre();
while(scanf("%d%d",&n,&m)!=EOF)
{
slen[0] = 0;
for(int i = 1; i<=n; i++)
{
scanf("%s", str[i]);
slen[i] = strlen(str[i]);
}
solve();
}
return 0;
}