1. 程式人生 > 其它 >題解 UVA1322 優化最大值電路 Minimizing Maximizer

題解 UVA1322 優化最大值電路 Minimizing Maximizer

題目傳送門

演算法分析:線段樹優化 dp

先提醒一下坑點:

  • 題中 Sorter 的順序不可改變。

一些約定:

  • 記第 \(k\) 個 Sorter 的左右端點為 \(l_k\)\(r_k\),長度為 \(len_k\)

首先分析 dp。我們關心的是能對目標序列全部排序的最少 Sorter 個數,但與第幾個無關,因此我們設計狀態 \(dp_i\) 表示將目標序列前 \(i\) 個數進行排序的最少 Sorter 個數(即右端點為 \(i\))。對於第 \(k\) 個 Sorter,有:

\[dp_{r_k}=\min(dp_j+1),j\in (l_k,r_k) \]

初始值為 \(dp_1=0,dp_i=inf,(i\ne1)\)

,邊界為 \(dp_n\)

此時,對於每一個 Sorter,都要進行 \(r_k-l_k+1\) 次檢查,時間複雜度為 \(\mathcal{O}(m\times \sum\limits_{k=1}^nlen_k)\)。顯然超時。

考慮優化轉移。

在暴力轉移時,我們是從一段連續區間內取一個最小值。需要區間修改,區間查詢最小值的操作。顯然,線段樹可以完成這個任務。於是我們用線段樹代替迴圈進行查詢和轉移。我們可以直接查詢 \(l_k\to r_k\) 這一段 \(dp\) 的最小值 \(x\),然後再把 \(x+1\) 作為新的 \(dp\) 值儲存到對應的區間上。最後答案就是 \(1\to n\)

的最小值。

具體見程式碼:

#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#define reg register
#define F(i,a,b) for(reg int i=(a);i<=(b);++i)
using namespace std;
inline int read();
const int N=5e5+5,inf=1e9;
int n,m,dp[N],ans;
struct P {
	int l,r;
} a[N];
namespace tree {
#define ls (x<<1)
#define rs (x<<1|1)
	const int P=5e4+10;
	struct P {
		int l,r,val,lz;
	} f[P<<2];
	void build(int x,int l,int r) {
		f[x]= {l,r,inf,inf};
		if(l==r) {
			f[x].val=inf;//賦極大值 
			return;
		}
		int mid=l+r>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
	}
	inline void push_down(int x) {
		if(f[x].lz==inf)return;
		//區間修改,取最小值 
		f[ls].val=min(f[ls].val,f[x].lz);
		f[ls].lz=min(f[ls].lz,f[x].lz);
		
		f[rs].val=min(f[rs].val,f[x].lz);
		f[rs].lz=min(f[rs].lz,f[x].lz);
		
		f[x].lz=inf;
	}
	int query(int x,int l,int r) {//查詢最小值 
		if(l<=f[x].l and f[x].r<=r)return f[x].val;
		push_down(x);
		int mid=f[x].l+f[x].r>>1,ans=m;
		if(l<=mid)ans=min(ans,query(ls,l,r));
		if(r>mid)ans=min(ans,query(rs,l,r));
		return ans;
	}
	void modify(int x,int l,int r,int val) {
		if(l<=f[x].l and f[x].r<=r) {
			f[x].val=min(f[x].val,val);
			f[x].lz=min(f[x].lz,val);
			return;
		}
		push_down(x);
		int mid=f[x].l+f[x].r>>1;
		if(l<=mid)modify(ls,l,r,val);
		if(r>mid)modify(rs,l,r,val);
		f[x].val=min(f[ls].val,f[rs].val);
	}
}
int main() {
	int T=read();
	while(T--) {
		n=read(),m=read();
		tree::build(1,1,n);//建樹並賦極大值 
		tree::modify(1,1,1,0);//賦值 dp[1]=0
		F(i,1,m) {
			a[i].l=read(),a[i].r=read();
			int x=tree::query(1,a[i].l,a[i].r);//查詢最小值 
			tree::modify(1,a[i].l,a[i].r,x+1);//轉移 
		}
		ans=tree::query(1,n,n);//最終答案 
		printf("%d\n",ans);
		if(T)putchar('\n');
	}
	return 0;
}
inline int read() {
	reg int x=0;
	reg char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x;
}

線段樹部分就是一個模板,還是十分容易寫的。

AC

歡迎交流討論,請點個贊哦~