开关问题(Switch_problem)

开关问题

也叫做“反转”,每一个点有有限个状态,可以操作某个点,改变若干点,最后所有点都变成同一状态,所需要的最小步数

题目链接

POJ3276
原题链接:点这里
在这里插入图片描述

算法思路

  1. 强行搜索的话,状态总共有 2 N 2^N 2N种,必然T
  2. 同一个位置从操作两次,是没有任何意义的,所以每个位置最多被当作头操作一次
  3. 通过一个sum来维护,前面对后面的影响,i每次移动一个,sum中可能减少的只有f[i-k+1](最左端),当然此处要判断下标是否大于0
  4. 因为最后k-1个无法被当作起点,所以需要检查合法性
  5. 每个长度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--) {
    //
    //	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值