題解:CF1840F Railguns

godmoo發表於2024-05-01

\(\texttt{Hint}\)

\(\texttt{Analysis}:\)

暴搜顯然不用想了,考慮 \(dp\)

有一個顯然的性質:\(t\) 不會超過 \(n+m+r\)

證明:因為沒有鐳射炮時,肯定需要 \(n+m\) 秒,而最多被一個鐳射炮“攔截”一次。

很容易想到 \(dp_{i,j,k}\) 表示能否\(k\) 時刻到達第 \(i\) 行第 \(j\) 列,則容易寫出狀態轉移方程:

\[dp_{i,j,k}=dp_{i-1,j,k-1}\operatorname{|}dp_{i,j-1,k-1}\operatorname{|}dp_{i,j,k-1} \]

利用滾動陣列壓掉一維:

\[dp_{i,j}=dp_{i-1,j}\operatorname{|}dp_{i,j-1}\operatorname{|}dp_{i,j} \]

即:

\[dp_{i,j}\operatorname{|=}dp_{i-1,j}\operatorname{|}dp_{i,j-1} \]

為保證無後效性,從後往前列舉即可。

那怎麼判斷無解呢?其實在轉移過程中記錄一下還有沒有 \(1\) 即可。因為如果沒有 \(1\) ,那麼再怎麼轉移都不可能出現 \(1\) 了。

時間複雜度 \(O(rlogr+nmt)\)

\(\texttt{Code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
const int R=105;
int t,n,m,r,cur;
struct Node{
	int t,d,c;
	bool operator <(const Node tmp) const{return t<tmp.t;}
}a[R];
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d%d",&n,&m,&r);
		int dp[n+5][m+5];memset(dp,0,sizeof dp); // 害怕炸空間可以這樣開
		for(int i=1;i<=r;i++) scanf("%d%d%d",&a[i].t,&a[i].d,&a[i].c);
		sort(a+1,a+r+1); // 按時間排序
		dp[0][0]=1;
		for(int tim=1,cur=1;;tim++){
			bool flag=0;
			// dp 過程,注意要倒序列舉
			for(int i=n;i>=0;i--){
				for(int j=m;j>=0;j--){
					if(dp[i][j]){
						flag=1;
						dp[i][j+1]=dp[i+1][j]=1;
					}
				}
			}
			// 判無解
			if(!flag){
				printf("-1\n");
				break;
			}
			/*
			鐳射炮的格子置0即可
			注意這段程式碼位置不能錯,因為在下一秒前離開是不會被打中的
			*/
			while(cur<=r&&a[cur].t==tim){
				if(a[cur].d==1) for(int i=0;i<=m;i++) dp[a[cur].c][i]=0;
				else for(int i=0;i<=n;i++) dp[i][a[cur].c]=0;
				cur++; 
			}
			if(dp[n][m]){
				printf("%d\n",tim);
				break;
			}
		}
	}
	return 0;
}