CodeForces-1680E Moving Chips
Moving Chips
线性dp
本来想用连通块求最短的方法,但是我一看到一个奇葩样例,直接否决这个想法,就是样例的第三个,那个倒三角
采取 dp 的方式:
设 \(dp[i][j]\),表示前 i 列只剩下一个芯片,且该芯片位于 第 i 列 第 j 行,所花费的最小代价
dp 的状态转移(以第 0 行为例):
-
如果在第 i 列的第 1 行存在芯片,则:\(dp[i][0] = min(dp[i-1][0], dp[i-1][1]) + 2\),因为不论是从哪里来,都得消除掉第 1 行的芯片,再加上一次的到第 0 行的代价,所以都是 + 2
-
如果在第 i 列的第 1 行不存在芯片,则:\(dp[i][0] = min(dp[i-1][0] + 1, dp[i-1][1] + 2)\),在同一行,直接移动过来,所以代价是 1,在不同行,移动过来代价是 2
对于第 1 行的话,也是一样的策略,所以直接用异或 和 for 循环替代了
这个 dp 的起点是从第一个芯片所在列开始,所以要先搜索到第一个芯片的位置,然后给这一列的 dp 初始化
如果两行都有芯片,则两边初始代价都是 1,如果只有一行有,则那一行的代价为 0,另一行的代价为 1 (需要将芯片移动过来)
同时答案也得不断地更新,理论上答案应该是出现芯片的最后一列的位置
这题和蓝桥杯省赛 B 组那个放积木那题挺像
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
const ll maxn = 2e5 + 10;
const ll inf = 1e17 + 10;
string s[2];
int dp[maxn][2];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
{
int n;
cin >> n;
cin >> s[0] >> s[1];
int l = 0;
while(s[0][l] != '*' && s[1][l] != '*') l++;
dp[l][0] = dp[l][1] = 0;
if(s[0][l] == s[1][l]) dp[l][0] = dp[l][1] = 1;
else if(s[0][l] == '*') dp[l][1] = 1;
else dp[l][0] = 1;
int ans = ans = min(dp[l][0], dp[l][1]);
for(int i=l+1; i<n; i++)
{
for(int j=0; j<2; j++)
{
if(s[j^1][i] == '*')
dp[i][j] = min(dp[i-1][j^1], dp[i-1][j]) + 2;
else
dp[i][j] = min(dp[i-1][j] + 1, dp[i-1][j^1] + 2);
}
if(s[0][i] == '*' || s[1][i] == '*') ans = min(dp[i][0], dp[i][1]);
// cout << dp[i][0] << " " << dp[i][1] << endl;
}
cout << ans << endl;
}
return 0;
}