CodeForces CF1846G 题解

这篇具有很好参考价值的文章主要介绍了CodeForces CF1846G 题解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

CodeForces CF1846G 题解

  • CodeForces题目链接

  • 洛谷题目链接

  • 标准答案是状压之后,转化成Dijkstra算法跑最短路。我这里提供一个不一样的思路。

题意简述

主人公得了病,有部分类型的症状。所有类型症状共有 \(n\) 种,用长为 \(n\) 的01串表示是否有那种症状。共有 \(m\) 种药,吃第 \(i\) 种药需要花费时间 \(t_i\), 能够治好症状 \(a_i\), 留下后遗症 \(b_i\), 其中 \(a_i\)\(b_i\)均为长度为 \(n\) 的01串,表示每种症状是否治好或者后遗。

主人公每次只能吃一种药。求康复需要的最少时间。

保证输入不会自相矛盾,药物能治好某种症状就不会后遗。

多组测试。

题目分析

1. 最后吃什么?

实际上这个过程和“化学除杂”有些类似。我们考虑最后吃的药的特征,发现最后吃的药一定没有后遗症。简单的证明就是:我们考虑症状个数 \(cnt\),最终目标是 \(cnt = 0\),如果每种药物都有后遗症,那么即使能够治好全部症状,也至少会遗留下一种后遗症,于是 \(cnt \ge 1\),矛盾。我们暂且把这种药物成为“纯药”(无后遗症)。

2. 交换吃药顺序?

我们发现交换药物服用顺序是不行的(显然后吃“纯药”和先吃“纯药”,一个康复,一个可能不康复)。

3. 一种药物吃几次?

接下来我们尝试考虑一种药物吃几次。

假设一个药物吃两次及以上,为了方便表示,我们不妨交换每种症状的相对位置,使得对于这个药物而言,是“治疗症状、保持原状、后遗症”的格式。例如原来是:

\[\begin{array}{} \text{主人公症状} & \texttt{01011}\\ \text{药物的疗效} & \texttt{11010}\\ \text{药物后遗症} & \texttt{00100}\\ \end{array} \]

交换症状相对位置之后(此处3、4列和4、5列对调)变成:

\[\begin{array}{} \text{主人公症状} & \texttt{01110} \\ \text{药物的疗效} & \texttt{11100}\\ \text{药物后遗症} & \texttt{00001}\\ \end{array} \]

我们将药物的效果压缩成一个串来表示,治疗用 \(\texttt{-}\) 表示,保持不变用 \(\texttt{0}\) 表示, 后遗症用 \(\texttt{+}\) 表示,于是:

\[\begin{array}{} \text{药物的疗效} & \texttt{11100}\\ \text{药物后遗症} & \texttt{00001}\\ \text{药物效果} & \texttt{---0+}\\ \end{array} \]

我们将不确定的位置用 \(\texttt{Q}\) 来占位表示。(下面表中的各部分串的长度仅为示意,实际上是某一特定的数值。)假如一个药物吃了两次及以上,肯定存在两次吃某一个药,于是有:

\[\begin{array}{} \text{项目} & \text{可治疗} & \text{不变} & \text{后遗症} \\ \text{用药前一状态} & \texttt{QQQ} & \texttt{QQQQ} & \texttt{QQ} \\ \text{药物效果} & \texttt{---} & \texttt{0000} & \texttt{++} \\ \text{一次用药后状态} & \texttt{000} & \texttt{QQQQ} & \texttt{11} \\ \text{中间若干用药} & \cdots & \cdots & \cdots \\ \text{二次用药后状态} & \texttt{000} & \texttt{QQQQ} & \texttt{11} \\ \end{array} \]

我们发现在两次服药中间的步骤,所起到的效果(或者说吃它们的目的),是为了改变 \(Q\) 的值。因此我们发现,如果把第一次吃药这一步撤掉,我们的结果是:

\[\begin{array}{} \text{项目} & \text{可治疗} & \text{不变} & \text{后遗症} \\ \text{用药前一状态} & \texttt{QQQ} & \texttt{QQQQ} & \texttt{QQ} \\ \text{药物效果} & \texttt{---} & \texttt{0000} & \texttt{++} \\ \text{中间若干用药} & \cdots & \cdots & \cdots \\ \text{原二次用药后状态} & \texttt{000} & \texttt{QQQQ} & \texttt{11} \\ \end{array} \]

效果没有改变。

因此一种药物吃一遍就足够了。也就是说,每种药只吃一次。

4. 从最后的药物出发

所以我们找到一个“纯药”之后,根据上面的结论,这个纯药应当在最后吃,而且只在最后吃(因为每种药只吃一次)。

我们观察一下:

\[\begin{array}{} \text{项目} & \text{可治疗} & \text{不变} & \text{后遗症} \\ \text{某状态} & \texttt{QQQ} & \texttt{QQQQ} & \texttt{QQ} \\ \text{中间若干用药} & \cdots & \cdots & \cdots \\ \text{纯药效果} & \texttt{---} & \texttt{0000} & \texttt{00} \\ \text{吃纯药后状态} & \texttt{000} & \texttt{QQQQ} & \texttt{QQ} \\ \end{array} \]

我们发现,吃纯药后把“可治疗”症状全部归 \(\texttt{0}\),也就意味着,如果最后吃这个“纯药”,那么再考虑之前的药物时,不用考虑“可治疗”的那几个症状,因为最后都会被纯药一次性全治好。

因此,我们把纯药从所有药物中删除,所有的药物和主人公症状中,涉及到所删除纯药“可治疗”的症状全部抹去,就转化成了规模更小的问题。我们暂时称这些位置“被覆盖了”。 如表格所示:

\[\begin{array}{} \text{项目} & \text{不变} & \text{后遗症} \\ \text{某状态} & \texttt{QQQQ} & \texttt{QQ} \\ \text{中间其他若干用药} & \cdots & \cdots \\ \end{array} \]

于是我们重复上述过程,在剩下的位置中,找剩下药物中的“纯药”(只考虑剩下的位置来判断)。

最终我们可以求得答案。

5. 具体实现的一些细节

具体实现中,我采用的是类似SPFA的算法(用优先队列,或者说是BFS也行),以及状态更新。我们令状态压缩的01串 \(S\) 表示每一个位置(症状)是否被覆盖。令 \(f_S\) 表示 \(S\) 状态下的最短时间。我们在更新的时候,除了更新 \(S\) 本身外,还要更新其“包含”状态的值(例如 \(\texttt{11001110}\) 中间包含 \(\texttt{10001010}\)):

\[f_{S'} \gets f_{S} , S' \subseteq S \]

由于使用优先队列,所以每个状态只访问一次,对应的vis数组记录,判断重复。

其他的位运算等细节请见代码。

记得没病要特判。

代码

#include <bits/stdc++.h>

#define N (int)(12)
#define M (int)(1e3 + 5)

using namespace std;

typedef long long LL;

struct drag
{
	LL t,e,se,idx;
}d[M];

LL f[1<<N];
bool vis[1<<N];

LL T;

LL n,m;

string to_str(int x);

struct state
{
	LL e,t;
};

bool operator<(const state xx, const state yy)
{
	return xx.t > yy.t;

priority_queue<state> q;

void dfs(LL e,LL p, LL t)
{
	if(vis[e]) return;
	if(e < (1<<p))
	{
		vis[e] = true;
		f[e] = t;
		return;
	}
	if(((e>>p)&1) == 1)
	{
		dfs(e^(1<<p),p+1,t);
	}
	dfs(e,p+1,t);
}

LL ansdfs(LL e,LL p)
{
	if(e < (1<<p))
	{
		return f[e];
	}
	LL ans = 1e18;
	if(((e>>p)&1) == 0)
	{
		ans = min(ans,ansdfs(e|(1<<p),p+1));
	}
	ans = min(ans,ansdfs(e,p+1));
	return ans;
}

inline void setf(LL e,LL t)
{
	dfs(e,0,t);
}

inline LL anti(LL x)
{
	return (1<<n) - 1 - x;
}

bool check(LL e,LL se)
{
	for(LL i = 0;i < n;i++)
	{
		if((((e>>i)&1) == 0) && (((se>>i)&1) == 1))
		{
			return false;
		}
	}
	return true;
}

string to_str(int x)
{
	string str = "";
	for(int i = 0;i < n;i++)
		str += ((x>>i)&1) + '0';
	return str;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> T;
	while(T--)
	{
		memset(vis,0,sizeof(vis));
		memset(f,0x7f,sizeof(f));
		cin >> n >> m;
		string str;
		cin >> str;
		LL e0 = 0;
		for(LL j = n - 1;j >= 0;j--)
				e0 = (e0 << 1) | (str[j] - '0');
		for(LL i = 1;i <= m;i++)
		{
			cin >> d[i].t;
			d[i].idx = i;
			cin >> str;
			d[i].e = 0;
			for(LL j = n - 1;j >= 0;j--)
				d[i].e = (d[i].e << 1) | (str[j] - '0');
			cin >> str;
			d[i].se = 0;
			for(LL j = n - 1;j >= 0;j--)
			{
				d[i].se = (d[i].se << 1) | (str[j] - '0');
			}
		}
		if(e0 == 0)
		{
			cout << "0\n";
			continue;
		}
		q.push({0,0});
		while(!q.empty())
		{
			state top = q.top();
			q.pop();
			if(!vis[top.e])
			{
				setf(top.e,top.t);
				for(int i = 1;i <= m;i++)
				{
					if(check(top.e,d[i].se))
					{
						LL ne = top.e | d[i].e;
						if(!vis[ne])
						{
							q.push({ne,top.t + d[i].t});
						}
					}
				}
			}
		}
		LL ans = ansdfs(e0,0);
		if(ans >= 1e9) cout << "-1\n";
		else cout << ans << "\n";
	}
	return 0;
}

本人能力有限,欢迎大家来Hack!文章来源地址https://www.toymoban.com/news/detail-633197.html

到了这里,关于CodeForces CF1846G 题解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • CF1011A Stages 题解

    题目意思: 给你一个长度为 n n n 的字符串 a a a ,在这个字符串中选一个长度为 k k k 的好串(好串标准是啥自己去题目里看吧),问这个好串的最小价值是多少。 思路: 贪心。 首先我们将字符串 a a a 里面的字符进行排序。 因为要最小的价值,所以排好序后的 a a a 的第一个

    2024年02月12日
    浏览(29)
  • 洛谷 CF1743APassword 题解

    https://www.luogu.com.cn/problem/CF1743A 已知一个长度为四的,只包含字符 0 , 1 , 2 , … , 9 0,1,2,dots ,9 0 , 1 , 2 , … , 9 的字符串中不会出现哪些字符,求可能的字符串的数量。 Monocarp has forgotten the password to his mobile phone. The password consists of $ 4 $ digits from $ 0 $ to $ 9 $ (note that it can start wit

    2023年04月20日
    浏览(44)
  • CF338D GCD Table 题解

    你有一个长度为 (k) 的数列 (a) , 询问是否存在 (xin[1,n]~~~yin[1,m]) 使得 (forall i~~~ gcd(x,y+i-1)=a_i) 。 我们转换一下可以得到: [forall i ~~left{ begin{matrix}xequiv 0pmod{a_i} \\\\y+i-1equiv 0pmod{a_i}end{matrix}right.] 前面一个 (x) 很好解决,直接 最大公倍数 。 (y) 可以转化一下:

    2024年02月07日
    浏览(37)
  • CF1145G AI Takeover 题解

    人工智能取得了进展。在这一题中,我们考虑的是石头剪刀布游戏。 然而,比赛的前一天晚上有一群人把机器人弄坏了,于是使用一个程序替代。 您需要找到一个策略,使您能够获胜。祝你好运! 为了方便,石头剪刀布分别用三个字符表示: R , P , S 。 本题有 6 个测试点,

    2024年03月26日
    浏览(41)
  • CF1178F1 Short Colorful Strip 题解

    题目描述 这是F题的第一个子任务。F1和F2的区别仅在对于m和时间的限制上 有n+1种颜色标号从0到n,我们有一条全部染成颜色0的长为m的纸带。 Alice拿着刷子通过以下的过程来给纸带染色: 我们按照从1到n的顺序进行染色,进行每次染色时,我们选取一个区间[ai,bi],0=aibi=m,并

    2024年02月01日
    浏览(36)
  • CF963B Destruction of a Tree 题解

      洛谷题目链接   这里提供一个较为朴素的 DP 想法。   给定一棵树,节点个数不超过 (2 times 10^5) ,每次可以删掉度数为偶数的点。问最后能不能删完;能删完给出删除方案。   首先可以随便选一个点作为根。   其次,我们考虑在一棵子树的删除情况,我们令

    2024年02月08日
    浏览(37)
  • Codeforces Round 877 (Div. 2) 题解

    写了两题了,先总结一下吧。。。看了三题,都是思维题构造题,搞心态真的搞心态。。。感觉自己屁都不会,完全没有动力。。。有的有思路但是写不出来,但是思路不够成熟,练的题太少,其实这场到B题就卡住了,C题也是,题意都能读懂,但是思考的方向不对,很焦虑

    2024年02月10日
    浏览(39)
  • Codeforces Round 889 (Div. 2) 题解

    晚上睡不着就来总结一下叭~(OoO) 终榜!!! 先不放图了。。怕被dalaoHack...呜呜呜~         7.29半夜比赛,本来是不想打的,感觉最近做的题太多了,什么牛客杭电以及CF上的题等等等,挺杂的,也来不及整理,本想梳理一下,而且也感觉今天状态不太好,但见他们都要

    2024年02月15日
    浏览(46)
  • Codeforces Round 858 (Div. 2)题解

    目录 A. Walking Master(贪心) 题面翻译 思路: 代码实现  B. Mex Master(贪心) 题面翻译: 思路: 代码实现 C. Sequence Master(数学) 题面翻译: 思路: 代码实现 YunQian is standing on an infinite plane with the Cartesian coordinate system on it. In one move, she can move to the diagonally adjacent point on the

    2023年04月08日
    浏览(77)
  • Codeforces Round 875 (Div. 1) 题解

    You are given two arrays a and b both of length n. Your task is to count the number of pairs of integers ( i , j ) (i,j) ( i , j ) such that 1 ≤ i j ≤ n 1≤ij≤n 1 ≤ i j ≤ n and a i ⋅ a j = b i + b j a_i⋅a_j=b_i+b_j a i ​ ⋅ a j ​ = b i ​ + b j ​ 。 1 ≤ a i , b i ≤ n 1≤a_i,b_i≤n 1 ≤ a i ​ , b i ​ ≤ n 考虑 m i n ( a i

    2024年02月08日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包