\(\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;
}