20241029每日一題洛谷P1024

才瓯發表於2024-11-02

普及-每日一題洛谷P1024

有形如:\(a x^3 + b x^2 + c x + d = 0\) 這樣的一個一元三次方程。給出該方程中各項的係數(\(a,b,c,d\) 均為實數),並約定該方程存在三個不同實根(根的範圍在 \(-100\)\(100\) 之間),且根與根之差的絕對值 \(\ge 1\)。要求由小到大依次在同一行輸出這三個實根(根與根之間留有空格),並精確到小數點後 \(2\) 位。

提示:記方程 \(f(x) = 0\),若存在 \(2\) 個數 \(x_1\)\(x_2\),且 \(x_1 < x_2\)\(f(x_1) \times f(x_2) < 0\),則在 \((x_1, x_2)\) 之間一定有一個根。

輸入

一行,\(4\) 個實數 \(a, b, c, d\)

輸出

一行,\(3\) 個實根,從小到大輸出,並精確到小數點後 \(2\) 位。

樣例輸入

1 -5 -4 20

樣例輸出

-2.00 2.00 5.00

經典的二分求值問題,這道題題目限制了根的範圍:根與根之差的絕對值 \(\ge 1\)根的範圍在 \(-100\)\(100\) 之間

於是,我們的大致流程就是:

  • 透過\(f(x_1) \times f(x_2) < 0\),尋找到一個區間\((x_1, x_2)\)使得其中必存在一個根

  • 由於根與根之差的絕對值 \(\ge 1\) ,所以尋找區間時,可以以 \(1\) 作為一個跨度

  • 再使用二分法,在含根區間內找到一個近似根

  • 題目要求:精確到小數點後 \(2\),這就存在兩種找到根的途徑:

    • 二分的mid代入方程中,直接可解出 0

    • 透過左邊界l和右邊界r向根無限逼近,存在一個精度限制

      透過題目可知:精度需要在小數點後兩位,所以在逼近根的時候,只需要控制左邊界和右邊界的差小於等於\(0.001\)

      這時不妨舉例有:\(l=1.005,r=1.006,root=1.00\) 任意選取l或r作為近似根,保留兩位小數後,與根的大小几乎相同,可以認為找到了一個根

程式碼首先實現\(f(x)\)的操作:

double fx(double x) {
	return a * pow(x, 3) + b * pow(x, 2) + c * x + d;
}

構建fx函式,方便之後呼叫求值

然後實現在區間內尋找根的函式find

double find(double l, double r) {
	while (r-l>0.001)//控制近似根的誤差小於等於0.001
	{
		double mid = (l + r) / 2;
		if (fx(l) * fx(mid) <= 0)
			r = mid;
		else
			l = mid;
	}
	return l;//返回l或r都可以
}

最後執行在\([-100,100]\)之間尋找含根區間:

for (int i = -100; i <= 100; i++) {
	double l = i, r = i + 1;
	double x1 = fx(l), x2 = fx(r);
	if (x1 == 0) {//判斷端點值是否恰好為根
		printf("%.2lf ", l);
		cnt++;
		continue;
	}//只用判斷左端點
	if (x1 * x2 < 0) {
		printf("%.2lf ", find(x1,x2));
		cnt++;//記錄根的個數
	}
	if (cnt == 3) return 0;//根全部解完後直接退出
}

完整AC程式碼:

double a, b, c, d;
double fx(double x) {
	return a * pow(x, 3) + b * pow(x, 2) + c * x + d;
}

double find(double l, double r) {
	while (r-l>0.001)
	{
		double mid = (l + r) / 2;
		if (fx(l) * fx(mid) < 0)
			r = mid;
		else
			l = mid;
	}
	return l;
}

int main()
{
	scanf("%lf %lf %lf %lf", &a, &b, &c, &d);
	int cnt = 0;
	for (int i = -100; i <= 100; i++) {
		double l = i, r = i + 1;
		double x1 = fx(l), x2 = fx(r);
		if (x1 == 0) {
			printf("%.2lf ", l);
			cnt++;
			continue;
		}
		if (x1 * x2 <= 0) {
			printf("%.2lf ", find(x1,x2));
			cnt++;
		}
		if (cnt == 3) return 0;
	}
	return 0;
}