开关问题
也叫做“反转”,每一个点有有限个状态,可以操作某个点,改变若干点,最后所有点都变成同一状态,所需要的最小步数
题目链接
POJ3276
原题链接:点这里
算法思路
- 强行搜索的话,状态总共有 2 N 2^N 2N种,必然T
- 同一个位置从操作两次,是没有任何意义的,所以每个位置最多被当作头操作一次
- 通过一个sum来维护,前面对后面的影响,i每次移动一个,sum中可能减少的只有f[i-k+1](最左端),当然此处要判断下标是否大于0
- 因为最后k-1个无法被当作起点,所以需要检查合法性
- 每个长度k都搜索一遍,时间复杂度 O ( N 2 ) O(N^2) O(N2)
代码实现
/*开关问题,奶牛的头全部朝前面*/
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <queue>
using namespace std;
#define el '\n'
#define cl putchar('\n')
#define eb emplace_back
#define fir first
#define sec second
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vci;
typedef map<int, int> mii;
typedef mii::iterator mii_it;
const int N = 1e5 + 10, M = 1e3 + 10;
int T, n, m, x, y;
int a[N];
bool dir[N];
bool f[N];
int calc(int k)
{
memset(f, 0, sizeof f);
int res = 0;
int sum = 0;
for (int i = 0; i + k <= n; i++)
{ //计算区间[i,i+k-1]
if ((dir[i] + sum) % 2 != 0)
{
//当前位置需要翻转
res++;
f[i] = 1;
}
sum += f[i];
if (i - k + 1 >= 0)
{ //最前面的那个影响消除了,类似优先队列
sum -= f[i - k + 1]; //右端-len+1==左端
}
}
//检查剩下的序列是否合法,因为不能以他们为头进行翻转
for (int i = n - k + 1; i < n; i++)
{
if ((dir[i] + sum) % 2 != 0)
return -1; //当前解不合法
if (i - k + 1 >= 0)
{
sum -= f[i - k + 1];
}
}
return res;
}
void solve()
{
int K = 1, M = n;
for (int k = 1; k <= n; k++)//原本k==2开始wa了,改成1就a了,因为k==1的时候需要<=n次翻转就能完成
{
int m = calc(k);
if (m >= 0 && M > m)
{
M = m, K = k;
}
}
cout << K << " " << M << el;
}
int main()
{
cin.tie(0);
cout.tie(0);
while (cin >> n)
{
char c;
for (int i = 0; i < n; i++)
{
cin >> c;
dir[i] = c == 'F' ? 0 : 1; //0代表朝前,1代表朝后面,最后要全部朝前面
}
solve();
}
// cin>>T;
// while(T--) {
//
// }
}