题解 SP196 【MUSKET - Musketeers】

智子

2019-07-28 16:22:52

Solution

在模拟赛中遇到了这道题。(后来才知道是SPOJ上的原题) 话不多说,开始动态规划三步走。$Let's\ go!$ ## 定义状态 假设第1个人能够赢得整场决斗: 倘若把这位仁兄复制一份,放在$n + 1$的;那么,在一阵厮杀后,他和自己的分身应当能够相遇。那么,我们就和 在[[NOI1995]石子合并](https://www.luogu.org/problem/P1880)中一样,将数组翻倍后再处理。 ~~显而易见~~定义状态如下: $dp_{i,j}$为第$i$人与第$j$人是否能够相遇 ## 状态转移方程 现在思考一下:第$i$人与第$j$人是否能够相遇? 按照区间DP的思维,我们在$i$与$j$之间选取一个人$k$ 若$i$与$k$能相遇,$k$与$j$能相遇,且$i$与$j$当中的任何一个人能干掉$k$ 故状态转移方程为: $$dp_{i,j} = dp_{i,k} \&\&\ dp_{k,j} \&\&\ (w_{i,k} || w_{j,k})$$ ## 边界条件 ~~显然,~~ 若两人本来就相邻,则$dp_{i,j} = 1$ ## 代码 ```cpp #include<iostream> #include<cstring> using namespace std; const int MAXN = 100 * 2 + 5; int w[MAXN][MAXN], f[MAXN][MAXN]; int n; int main() { int t; cin >> t; while(t--) { memset(f, 0, sizeof(f)); //数组清零,我在这里掉了两次坑 memset(w, 0, sizeof(w)); cin >> n; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { char c; cin >> c; w[i][j] = c - '0'; w[i + n][j + n] = w[i + n][j] = w[i][j + n] = w[i][j]; } } for(int l = 1; l <= n + 1; l++) { for(int i = 1; i + l - 1 <= n * 2; i++) { int j = i + l - 1; if(l <= 2) { f[i][j] = 1; //边界条件 continue; } for(int k = i; k <= j; k++) { if(f[i][k] && f[k][j] && (w[i][k] || w[j][k])) { f[i][j] = 1; break; } } } } int ans = 0; for(int i = 1; i <= n; i++) { if(f[i][i + n]) { ans++; } } cout << ans << endl; for(int i = 1; i <= n; i++) { if(f[i][i + n]) { cout << i << endl; } } } return 0; } ```