【DP】系统可靠性 题目+题解

本博客探讨了一个系统可靠性的问题,系统由多个部件串联组成,任何一个部件故障都会导致系统无法正常运行。为了提高可靠性,每个部件都有备用件。在给定的总费用限制下,如何在备用件数量与系统可靠性之间找到最佳平衡?博客提供了输入输出格式,并展示了如何使用动态规划方法求解这个问题,以达到最高的系统可靠性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

系统可靠性 (简单)

TimeLimit:1000MS  Memory Limit:65536K
Total Submit:1 Accepted:1

Description

一个系统由若干部件串联而成,只要有一个部件故障,系统就不能正常运行,为提高系统的可靠性,每一部件都装有备用件,一旦原部件故障,备用件就自动进入系统。显然备用件越多,系统可靠性越高,但费用也越大,那么在一定总费用限制下,系统的最高可靠性等于多少?
给定一些系统备用件的单价Ck,以及当用Mk个此备用件时部件的正常工作概率Pk(Mk),总费用上限C。求系统可能的最高可靠性。

Input

第一行:n C
第二行:C1 P1(0) P1(1) …P1(X1) (0<=X1<=[C/Ck])

第 n 行:Cn Pn(0) Pn(1) … Pn(Xn) (0<=Xn<=[C/Cn])

Output

系统可能的最高可靠性。

Sample Input

2 20
3 0.6 0.65 0.7 0.75 0.8 0.85 0.9
5 0.7 0.75 0.8 0.8 0.9 0.95

Sample Output

0.6375

Hint

DP

资源分配类型

 

附:题解+代码

program systemcode;

var
	n,money:longint;
	a:array [0..100,-1..100] of real;//用a[i,j]表示第i项备用件花了j*i元能取得的最高可靠性,特别地,a[i,-1]表示这项备用件的费用,a[i,0]表示不花钱或者买不起一个第i项备用件所得到的最高可靠性
	f:array [0..100,0..100] of real;//用f[i,j]表示前i项备用件用j元钱能取得的最高可靠性
	
procedure init;
var
	i,j:longint;
begin
	readln(n,money);
	for i:=1 to n do
		begin
			read(a[i,-1]);
			for j:=0 to (money div trunc(a[i,-1])) do read(a[i,j]);
			readln;
		end;
end;

function max(x,y:real):real;
begin
	if x>y then exit(x) else exit(y);
end;

procedure main;
var
	i,j,k,tx:longint;
	temp:real;
begin
	temp:=1;
	for i:=0 to money do 
		begin
			tx:=i div trunc(a[1,-1]);//这里是对应买了几个第1项备用件
			if tx>0 then 
				begin
					f[1,i]:=a[1,tx];//买得起则对应标号
					continue;
				end
			else 
				begin 
				f[1,i]:=a[1,0]; //买不起则对应不花钱的情况
					continue; 
				end;
		end;
	for i:=1 to n do 
		begin
			temp:=a[i,0]*temp;
			f[i,0]:=temp;
		end;
	for i:=2 to n do//i=1时我们已经人工算出了每个情况的最高可靠性
		begin
			for j:=0 to money do
				begin
					tx:=j div trunc(a[i,-1]);//当前的钱能买几个第i项备用件
					for k:=0 to tx do f[i,j]:=max(f[i-1,j-k*trunc(a[i,-1])]*a[i,k],f[i,j]);//这些钱我们可以一点都不用在买第i项备用件,或者全用来买第i项备用件,或者买k个第i项备用件
					//DP方程:f[i,j]=max{f[i-1,j-k*trunc(a[i,-1])]*a[i,k],f[i,j]},这里f[i,j]主要是用来被覆盖更新的,在初始情况下,它一定会被f[i-1,j-k*trunc(a[i,-1])]替换,此后通过打擂台更新f[i,j]
				end;
		end;
end;

begin
	assign(input,'systemcode.in'); reset(input);
	assign(output,'systemcode.out'); rewrite(output);

	init;
	main;
	writeln(f[n,money]:0:4);

	close(input); close(output);
end.


 

请结合此题解分析并解释此代码一个 n×m 的 01 矩阵,其中 k 个位置的值给定。这个矩阵满足性质:对于每个 2×2 的小子矩阵中 0 与 1 的个数相同。求满足条件的矩阵数量,对 10 9 +7 取模。 n,m≤10 9 ,k≤10 5 。 一个显然的事实是:确认了第一行和第一列,就能推得所有矩阵。考虑如何确定第一行和第一列。 结论:一个矩阵是一个合法的矩阵,当且仅当其满足,要么第一行是 010101… 或 101010… 这样 01 交错的;要么第一列是这样 01 交错的。 证明可以画图,考虑一个这样的矩阵,使得其第一行和第一列都不是 01 相间的。那么可以找出一个位置 i,满足第 i 行的第一个数和第 i+1 行的第一个数相同(即第一列上连续的两个相同的数),为了方便,都假定为 1。我们考虑这两行的情况: 101⋯01101⋯1001⋯ 101⋯01101⋯1001⋯ ​ (由于 Latex 渲染问题,该公式建议在博客内查看) 注意到红色 1 的位置,在这个位置为了满足以它作为右下角的矩阵而值为 1,但这就导致了以它为左下角的矩阵出现了三个 1,成为了一个不合法的矩阵。这正是因为行上也有相邻的两个 1,有两个相邻的 0 也是这种情况(蓝色部分)。这是不可避免的,故而这样的矩阵都是不合法的。而如果不出现这种情况,则一定可以构造出合法方案。故而结论证毕。 同时这个结论可以推导出一些东西:对于一个第一行都是 01 相间的矩阵,它的每一行都是 01 相间的;对于一个第一列都是 01 相间的矩阵,它的每一列都是 01 相间的。 接下来考虑如何计数。根据容斥原理,我们用 行都是 01 相间的矩阵 + 列都是 01 相间的矩阵 − 行列都是 01 相间的矩阵(即类似棋盘的黑白染色矩阵)。 行都是 01 相间和列都是 01 相间做法类似,这里只介绍行的方式,列做法同理。 对于行上没有已确定的 k 个位置,这一行的情况可以任取 0101… 或 1010… 的一种,乘法原理对答案乘 2。 对于行上已经有确定的位置时,用奇偶性检测它们能否构成 01 相间的一行。如果可以,对答案乘 1;如果不行,答案为 0。 行列都是 01 相间的矩阵直接检查是否能够构成一个棋盘状的东西,同样判一下奇偶性。注意特判一下 k=0 的情况。 这题这样就做完了,时间复杂度 O(klogk+log(n+m))。 代码: #include <algorithm> #include <cstdio> #include <cstring> const int MaxN = 100000; const int Mod = 1000000007; inline int add(int x, int y) { return (x += y) >= Mod ? x - Mod : x; } inline int sub(int x, int y) { return (x -= y) < 0 ? x + Mod : x; } inline int mul(int x, int y) { return 1LL * x * y % Mod; } inline int pw(int x, int y) { int z = 1; for (; y; y >>= 1, x = mul(x, x)) if (y & 1) z = mul(z, x); return z; } inline int inv(int x) { return pw(x, Mod - 2); } inline int sep(int x, int y) { return mul(x, inv(y)); } inline void inc(int &x, int y = 1) { x = add(x, y); } inline void dec(int &x, int y = 1) { x = sub(x, y); } struct data_t { int x, y, d; data_t(int _x = 0, int _y = 0, int _d = 0) { x = _x, y = _y, d = _d; } }; int W, H, N; data_t A[MaxN + 5]; inline int getSit() { char c; do c = getchar(); while (c != '+' && c != '-'); return (c == '+') ? 1 : 0; } void init() { scanf("%d %d %d", &W, &H, &N); for (int i = 1; i <= N; ++i) { A[i].d = getSit(); scanf("%d %d", &A[i].x, &A[i].y); } } inline bool cmpx(const data_t &a, const data_t &b) { if (a.x != b.x) return a.x < b.x; else return a.y < b.y; } inline bool cmpy(const data_t &a, const data_t &b) { if (a.y != b.y) return a.y < b.y; else return a.x < b.x; } inline int calc1() { std::sort(A + 1, A + 1 + N, cmpx); int res = 0, prex = 0; for (int l = 1, r = 0; l <= N; l = r + 1) { while (r < N && A[r + 1].x == A[l].x) r++; for (int i = l; i <= r; ++i) { int x1 = (A[i].y ^ A[l].y) & 1, x2 = (A[i].d ^ A[l].d); if (x1 != x2) return 0; } res += A[l].x - prex - 1; prex = A[l].x; } res += W - prex; return pw(2, res); } inline int calc2() { std::sort(A + 1, A + 1 + N, cmpy); int res = 0, prey = 0; for (int l = 1, r = 0; l <= N; l = r + 1) { while (r < N && A[r + 1].y == A[l].y) r++; for (int i = l; i <= r; ++i) { int x1 = (A[i].x ^ A[l].x) & 1, x2 = (A[i].d ^ A[l].d); if (x1 != x2) return 0; } res += A[l].y - prey - 1; prey = A[l].y; } res += H - prey; return pw(2, res); } inline int calc3() { for (int i = 1; i <= N; ++i) { int x1 = (A[1].x ^ A[1].y ^ A[i].x ^ A[i].y) & 1, x2 = (A[1].d ^ A[i].d); if (x1 != x2) return 0; } return 1; } void solve() { int ans = 0; inc(ans, calc1()); inc(ans, calc2()); dec(ans, calc3()); if (N == 0) dec(ans); printf("%d\n", ans); } int main() { init(); solve(); return 0; }请以在代码中注释的方式解释此代码
07-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值