例题 9-23 有趣的游戏(Fun Game,ACM/ICPC Beijing 2004,UVa1204)

原题链接:https://vjudge.net/problem/UVA-1204
分类:状压DP
备注:字符串覆盖

按紫书的思路,首先排除被覆盖的串,每个串都试着正反两个状态,与当前集合的最后一个串计算覆盖量,取最优。这里固定以某一个串为第一个串,就可以很方便地来把最后一个串和第一个串连起来。

注意有效串个数为1,以及长度小于等于1应该变为2的情况。

#include <bits/stdc++.h>
using namespace std;

const int N = 20;
const int INF = 0x3f3f3f3f;

int n, len[N], cover[N][N][2][2], dp[1 << N][N][2];
// dp[s][i][o],其中s表示已经加入的字符串的集合,i表示结尾串的编号,o表示结尾串是正向还是逆向 
string s[N][2];

struct String {
	string s, rev;
	bool operator < (const String& rhs) const {
		return s.length() < rhs.s.length();
	}
}input[N];

int calc(const string& a, const string& b) {
	int len1 = a.length();
	int len2 = b.length();
	for (int i = 1; i < len1; i++) {
		if (i + len2 < len1) continue;
		int flg = 1;
		for (int j = 0; i + j < len1; j++) {
			if (a[i + j] != b[j]) {
				flg = 0;
				break;
			}
		}
		if (flg) return len1 - i;
	}
	return 0;
}

int main(void) {
//	freopen("in.txt", "r", stdin); 
//	freopen("out.txt", "w", stdout);
	while(cin >> n && n) {
		for (int i = 0; i < n; i++) {
			cin >> input[i].s;
			input[i].rev = input[i].s;
			reverse(input[i].rev.begin(), input[i].rev.end());
		}
		sort(input, input + n);
		int tot = 0, sumLen = 0;
		for (int i = 0; i < n; i++) {
			int covered = 0;
			for (int j = i + 1; j < n; j++) {
				if (input[j].s.find(input[i].s) != string::npos ||
				    input[j].s.find(input[i].rev) != string::npos) {
					covered = 1;
					break;    	
				}
			}
			if (covered) continue;
			s[tot][0] = input[i].s;
			s[tot][1] = input[i].rev;
			len[tot] = input[i].s.length();
			sumLen += len[tot];
			tot++;
		}
		for (int i = 0; i < tot; i++)
			for (int j = 0; j < tot; j++)
				for (int o1 = 0; o1 < 2; o1++) 
					for (int o2 = 0; o2 < 2; o2++) 
						cover[i][j][o1][o2] = calc(s[i][o1], s[j][o2]);
		memset(dp, -1, sizeof(dp));
		// 固定将第一个字符串正向摆放 
		dp[1][0][0] = 0;
		for (int sta = 1; sta < (1 << tot) - 1; sta++) 
			for (int e = 0; e < tot; e++) // 当前尾部串编号为e 
				for (int o = 0; o < 2; o++)
					if (dp[sta][e][o] >= 0)
						for (int j = 1; j < n; j++) {
							if (sta & (1 << j)) continue;
							for (int o2 = 0; o2 < 2; o2++)
								dp[sta | (1 << j)][j][o2] = max(dp[sta | (1 << j)][j][o2], dp[sta][e][o] + cover[e][j][o][o2]);
						}
		int maxCover = 0, ans;
		for (int i = 1; i < tot; i++)
			for (int o = 0; o < 2; o++)
				maxCover = max(maxCover, dp[(1 << tot) - 1][i][o] + cover[i][0][o][0]);
		if (tot == 1) ans = len[0] - cover[0][0][0][0];
		else ans = sumLen - maxCover;
		cout << (ans <= 1 ? 2 : ans) << endl;
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JILIN.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值