题目大意
有一种病有 n (1<= n <=10) 种症状,用一个01字符串表示,1代表有0代表没有。有m (1<= m <=10^3)种药,每种药的服药时间为di,上一种药的疗程没进行完时不能使用下一种药。每种药由两个01字符串来表示药效,一个代表能消除的病,一个代表会产生的副作用,两个01串不相交,即如果某种药物缓解了某种症状,那么它就不会出现在副作用中。求最少需要几天可以消除所有症状。药可以重复使用。若不能则输出 −1。
思路
n最大为10,每种病有两个状态,故最多有 2^10 (==1024) 种状态。每种药相当于一种连边方式,把所有状态和边都放到图上,跑一个从初始状态到状态0的最短路。
首先将所有的01串转为十进制数。考虑连边后状态如何转移,假如当前状态是 10101,吃的药能治好 10001,副作用为 01010,治好能得到00100,相当于10001取反之后和初始状态进行按位与运算,而最终得到的状态即为治愈的结果与副作用进行按位或运算。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1100, INF = 0x3f3f3f3f;
struct { int d, u, v; }a[N];//存药品信息
//01字符串转化为十进制数
int zh(string s) {
int tmp = 0;
for (int i = 0; i < s.size(); ++i)
tmp = tmp * 2 + (int)(s[i] - '0');
return tmp;
}
void solve() {
int n, m;
cin >> n >> m;
int k = 1 << n;//总的状态数
vector<int>dist(k, INF);
vector<bool>st(k,0);
string s1, s2, s3;
cin >> s1;
dist[zh(s1)] = 0;//初始状态
for (int i = 1; i <= m; ++i) {
cin >> a[i].d >> s2;
a[i].u = zh(s2);
cin >> s3;
a[i].v = zh(s3);
}
while (1) {
int t = -1;
//以所有的状态为节点
for (int i = 0; i < (1 << n); ++i) {
if (st[i] || dist[i] == INF) continue;
if (t == -1 || dist[i] < dist[t])
t = i;
}
if (t == -1) break;
st[t] = 1;
//每种药品都是一种建边方式
for (int i = 1; i <= m; ++i) {
int tmp = (t & (~a[i].u)) | a[i].v;//转移
dist[tmp] = min(dist[tmp], dist[t] + a[i].d);
}
}
if (dist[0] == INF) cout << -1 << '\n';
else cout << dist[0] << '\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}