【状压DP】Words

Description

Io和Ao在玩一个单词游戏。他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须 
与前一个单词的最后一个字母一致。游戏可以从任何一个单词开始。 
任何单词禁止说两遍,游戏中只能使用给定词典中含有的单词。 
游戏的复杂度定义为游戏中所使用的单词的长度总和。 
编写程序求出使用一本给定的词典来玩这个游戏所能达到的游戏最大可能复杂度。 
数据规模限制:单词总数不超过16,单词长度不超过100。 

Input

第一行给数字N,代表有多少个单词 
下面N行,每行一个单词

Output

最大可能复杂度

Sample Input

3
AE
A
IA

Sample Output

5

Solution

这题好像可以暴搜???

把每个单词看作一个节点,根据首字母和尾字母连边,单词长度作为节点权值,然后暴搜???

(写题解时的意外发现)

好像状态数大了点。。

反正当时是写的状压DP

首先根据首字母和尾字母预处理一下,判断任意两个单词是否可以互相到达

状态设计成两维dp[ i ][ j ]表示到达第 i 个单词时,状态为 j 的最大复杂度

状态转移方程

dp[i][m | (1 << (i - 1))] = max(dp[i][m | (1 << (i - 1))], dp[j][m] + len[i]);

Code

#include
using namespace std;

int n;
char s[20][210];
bool link[20][20];
int len[20];
int dp[20][(1 << 16) + 7];//连接到第i个单词 状态为j 的最大复杂度 
int ans;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
    return x * f;
}

int main() {
    n = read();
    for (int i = 1; i <= n; i++) {
	scanf("%s", s[i] + 1);
	len[i] = strlen(s[i] + 1);
	ans = max(ans, len[i]);
	dp[i][1 << (i - 1)] = len[i];
    }
    for (int i = 1; i <= n; i++) {
	for (int j = 1; j <= n; j++) {
	    if (i == j) continue;
	    if (s[i][1] == s[j][len[j]]) link[j][i] = true;
	    if (s[j][1] == s[i][len[i]]) link[i][j] = true;
	}
    }
    for (int m = 1; m < (1 << n); m++) {//now state
	for (int i = 1; i <= n; i++) {//next
	    if (m & (1 << (i - 1))) continue;
	    for (int j = 1; j <= n; j++) {//now
		if (i == j) continue;
		if (!link[j][i]) continue;
		if (m & (1 << (j - 1))) {
		    dp[i][m | (1 << (i - 1))] = max(dp[i][m | (1 << (i - 1))], dp[j][m] + len[i]);
		    ans = max(ans, dp[i][m | (1 << (i - 1))]);
		}
	    }
	}
    }
    printf("%d", ans);
}


 

你可能感兴趣的:(DP)