1. 程式人生 > 實用技巧 >洛谷 P1080 國王遊戲 題解

洛谷 P1080 國王遊戲 題解

原題

傳送門

思路

分析

我們先假設隊伍如下:

People left hand right hand
Before \(S_a\)
A \(a_1\) \(b_1\)
B \(a_2\) \(b_2\)
After \(S_b\)

現在我們要交換A、B,隊伍如下:

People left hand right hand
Before \(S_a\)
B \(a_2\) \(b_2\)
A \(a_1\) \(b_1\)
After \(S_b\)

我們可以發現:這樣交換對於BeforeAfter部分的結果沒有影響,只對A

B 的部分結果有影響。
對於交換前的答案:
\(ans1=\max\{\dfrac{S_a}{b_1},\dfrac{S_a\times a_1}{b_2}\}\)
對於交換後的答案:
\(ans2=\max\{\dfrac{S_a}{b_2},\dfrac{S_a\times a_2}{b_1}\}\)
我們知道,這些數都是大於等於\(1\)的正整數,於是:
\(\dfrac{S_a\times a_1}{b_2} \ge \dfrac{S_a}{b_2}\)

\(\dfrac{S_a\times a_2}{b_1} \ge \dfrac{S_a}{b_1}\)

因此,當我們假定 \(ans1 < ans2\)

時,一定是:
\(\dfrac{S_a\times a_1}{b_2} < \dfrac{S_a\times a_2}{b_1}\)
化簡得:
\(a_1\times a_2 < b_1 \times b_2\)

演算法

顯然,此關係滿足傳遞性,即當
\(a_1\times a_2 < b_1 \times b_2\)
\(b_1\times b_2 < c_1 \times c_2\)
有:
\(a_1\times a_2 < c_1 \times c_2\)
因此,我們可以貪心,只要按此規則排序,再統計答案即可,注意要高精

程式碼

壓位高精:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

#define ll long long

const int MAXN = 1010;
const int base = 10000;

int n;
struct people{
	int x,y;
	bool operator < (const people &b ) const {
		return (ll)x * y < (ll)b.x * b.y;
	}
}peo[MAXN];

struct bignum{//only mul ,comp ans so on
	int a[1020],len;
	bignum() {//初始化
		memset(a,0,sizeof(a));
		len = 1;
	}
	void resize(){//限制位數
		len = 1010;
		for(int i = len - 1;i >= 0;i--)
			if( a[i] > 0 ) { 
				len = i + 1;
				return;
			}
		len = 1;
	}
	bool operator < (const bignum &b) const{//比大小
		if ( len != b.len) return len < b.len;
		for(int i = len - 1 ;i >= 0 ;i--){
			if(a[i] != b.a[i]) return a[i] < b.a[i];
		}
		return 0;
	}
	
	bignum operator * (const int &b) const{ //乘法(高精乘int)
		bignum c;
		for(int i = 0; i < len ;i++){
			c.a[i] += a[i] * b;
			if( c.a[i] >= base) {
				c.a[i+1] += c.a[i]/base;
				c.a[i] %= base;
			}
		}
		c.resize();
		return c;
	}
	
	bignum operator / (const int &b) const{//除法(高精除int)
		ll temp = 0;
		bignum c;
		if( b == 0) {
			printf("Error!");
			return c;
		}
		for(int i = len -1;i >= 0;i--){
			temp = temp * base + a[i];
			if(temp >= b) {
				c.a[i] = temp / b;
				temp %= b;
			}
		}
		c.resize();
		return c;
	}
	
	void print (){//輸出
		resize();
		printf("%d",a[len-1]);
		if ( len > 1) 
		for(int i = len - 2;i >=0;i--){
			printf("%04d",a[i]);
		}
		return;
	}
};
int main (){
	scanf("%d",&n);
	for(int i = 0;i <= n;i++){
		scanf("%d %d",&peo[i].x,&peo[i].y);
	}
	sort(peo+1,peo+n+1);
	bignum ans,tot;
	tot.a[0] = 1 ;
	tot = tot * peo[0].x;
	for(int i = 1;i <= n;i++){
		bignum temp = tot / peo[i].y;
		tot = tot * peo[i].x;
		if( ans < temp ) ans = temp;
	}
	ans.print();
	return 0;
}

反思總結

應該是太久沒寫壓位高精了,打錯了INF回,我有幾個出錯的地方:

  1. resize() 函式裡未在最後設定len=1 ,導致數為0時len很大
  2. resize() 函式裡面把 return 寫成break; ,導致每個被resize()的數的len=1
  3. base可以去1e5但只取了1e4
  4. print()函式裡面輸出記憶錯誤:
printf("%4d",a[i]);

顯然,少了0
5. *函式裡面寫成了:

bignum operator * (const int &b) const{
		bignum c;
		for(int i = 0; i < len ;i++){
			c.a[i] = a[i] * b;
			if( c.a[i] >= base) {
				c.a[i+1] += c.a[i]/base;
				c.a[i] %= base;
			}
		}
		c.resize();
		return c;
	}

總的來說,我也要多練習壓位高精……