1. 程式人生 > 實用技巧 >暑 假 隊 測 Round #3

暑 假 隊 測 Round #3

\(100+20+0=120pts\)

T1:最小和
T2:括號序列
T3:貓狗大戰

T1:單調佇列優化dp

線性dp,設\(f[i]\)為前\(i\)個數的最小和(\(i\)被選擇)。
顯然:
\(f[i]=min\){\(~~f[j]+a[i]~~\)}\(~~(i>j>i-m)~~\),
拆一下:
\(f[i]=min\){\(~f[j]~\)}\(~\)\(+\)\(~\)\(a[i]\)\(~~(i>j>i-m)~~\),
已經很裸了,單調佇列維護最小的\(f[j]\).

\(T1のCode\)

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m,a[N],f[N],mn=INT_MAX,q[N];
int main(){
	//freopen("minn.in","r",stdin);
	//freopen("minn.out","w",stdout);
	memset(f,0x3f,sizeof(f));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	f[0]=0;
	int l=1,r=1;q[1]=0;
	for(int i=1;i<=n;i++){
		while(l<=r&&q[l]<i-m)l++;
		f[i]=f[q[l]]+a[i];
		while(l<=r&&f[q[r]]>=f[i])r--;
		q[++r]=i;
	}
	for(int i=n;i>=n-m+1;i--)mn=min(mn,f[i]);
	printf("%d",mn);
	return 0;
}

T2:區間dp

\(f[l][r]\)\(l\) ~ \(r\)中的合法序列方案數,
根據空串序列合法性,所以\(f[i][i-1]=1\),
最後答案是\(f[1][n]\)
很容易想到\(f[l][r]\)\(=\)\(\sum{f[l][k]+f[k+1][r]}\),

其實這個轉移方程是錯誤的,看這組樣例:

( ) ( ) ( )

這個樣例只有唯一的合法括號序列,可是在上述轉移中會出現重複計數。

所以在合併時,如有\(A\)\(B\)兩個合法括號序列,我們應保證是形如\(A\)\(~\)\(~\)\((\)\(~\)\(B\)\(~\)\()\)

所以有

  • \(f[l][r]+=f[l][k]*f[k+2][r-1]~(~s[k+1]==s[r]~~||~~s[k+1]==?~~||~~s[r]==?)\)
  • \(f[l][r]+=f[l][k]*f[k+2][r-1]*3~~(~~s[k+1]==s[r]==?~~)~~\)

\(T2のCode\):

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=205;
const int mod=1e5;
int n;
ll f[N][N];
char s[N];
char ls[3]= {'(','[','{'};
char rs[3]= {')',']','}'};
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		cin>>s[i];
	for(int i=1;i<=n;i++)f[i][i-1]=1;
	for(int len=2;len<=n;len+=2)
		for(int l=1;l<=n-len+1;l++){
			int r=l+len-1;
			for(int k=l-1;k<=r;k++)
				for(int t=0;t<3;t++)
					if((s[k+1]==ls[t]||s[k+1]=='?')&&(s[r]=='?'||s[r]==rs[t]))
						f[l][r]=(f[l][r]+((f[l][k]*f[k+2][r-1])%mod))%mod;
		}
	printf("%lld",f[1][n]%mod);
	return 0;
}

T3:單調佇列

其實是最水的一道。。可惜保齡收場。
仔細觀察發現解法複雜度與\(n<=10^9\)的驚人範圍無關。
\(m<=5\times10^3\),可以搞一個\(O(m^2)\)
思考:一個行李箱通過所有障礙的下限是什麼?
當然就是通過最狹窄的地方!
想到這個,我們可以列舉長度,然後單調佇列跑一遍,得到寬度,然後計算面積即可。

T3のCode:

#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
const int N=5e3+10;
using namespace std;
int n,m,a[N],b[N],q1[N],q2[N];
ll ans;
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)scanf("%d",&b[i]);
	for(int k=1;k<=m;k++){
		int d=k,mn=inf;
		int l1=1,r1=0,l2=1,r2=0;
		for(int i=1;i<=m;i++){
			while(l1<=r1&&i-d>q1[l1])l1++;
			while(l1<=r1&&a[q1[r1]]<a[i])r1--;
			q1[++r1]=i;
			while(l2<=r2&&i-d>q2[l2])l2++;
			while(l2<=r2&&b[q2[r2]]<b[i])r2--;
			q2[++r2]=i;
			if(i>=d)if(n-a[q1[l1]]-b[q2[l2]]<=0)mn=0;
			            else mn=min(mn,n-a[q1[l1]]-b[q2[l2]]);
		}
		ans=max(ans,1LL*mn*k);
	}
	printf("%lld",ans);
	return 0;
}