Codeforces Round 883 (Div. 3) G. Rudolf and CodeVid-23 最短路

题目链接

题目大意

        有一种病有 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;
}

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值