反转(开关问题) POJ 3276

POJ 3276

题意:n头牛站成线,有朝前有朝后的的,然后每次可以选择大小为k的区间里的牛全部转向,会有一个最小操作m次使得它们全部面朝前方。问:求最小操作m,再此基础上求k。

题解:1、5000头牛不是小数目,再怎么也得要n^2的算法,其中,枚举k是需要的,这就有n了,只能想办法给出一个n在O(n)时间内求出最小次数了。

   2、对于给定的k,要想O(n)内把次数算出来,即只能扫一遍,一想到的必定是从前往后扫,遇到面朝后的就转头,但这一转牵扯太多,要改太多东西,k一大直接崩溃。

   3、对于每次扫描到的第i个点,都至多只能改一次才能保证效率,即只改变化的。将牛的朝向弄成依赖型,即后者依赖于前者,这样在一个区间内[a,b]翻转时,实际上[a+1,b]的依赖关系是没有改变的,改变的只有a,b+1。

   4、综上,设置一种关系表示每头牛与前一头牛的朝向,最简单的就是同向与反向的差异,不妨令同向为0,反向为1,为了使得最后都朝前,可以令一头虚拟牛(即0号牛)头朝前,然后第一头牛依赖于它。

   5、因此,每次检查时,只需要更改a和a+k位置的牛的依赖关系便可以解决了,最后在检查一下剩余的牛是否全是0就结束了。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <memory.h>
#include <cstring>

using namespace std;

int N;
char tmp, tmp1;
int arr[5002];
int arr1[5002];

int K,M;

int test(int k)
{
    memcpy(arr1,arr,sizeof(arr));
    int time = 0;
    for(int i=1; i<=N-k+1; i++)
    {
        if(arr1[i] == 1)
        {
            arr1[i] = 0;
            arr1[i+k] = abs(arr1[i+k]-1);
            time++;
        }
    }

    for(int i=N-k+1; i<=N; i++)
    {
        if(arr1[i] == 1)
            return 0;
    }
    return time;

}

void caculate()
{
    K = 1;
    M = N;
    int tmpM;
    for(int i=1; i<=N; i++)
    {
        tmpM = test(i);

        if(tmpM>0 && tmpM<M)
        {
            M = tmpM;
            K = i;
        }
    }

}

int main(int argc, char** argv)
{

    //freopen("E:/sample_input.txt", "r", stdin);

    while(scanf("%d",&N)!=EOF)
    {
        memset(arr, 0, sizeof(arr));
        tmp1 = ‘F‘;
        for(int i=1; i<=N; i++)
        {

            scanf(" %c", &tmp);
            if(tmp == tmp1)
                arr[i] = 0;
            else
                arr[i] = 1;
            tmp1 = tmp;
        }

        caculate();

        cout << K << " " << M << endl;

    }
    return 0;
}
时间: 2024-11-10 23:32:17

反转(开关问题) POJ 3276的相关文章

POJ 3276 Face The Right Way (常用技巧-尺取法)

[题目链接]:click here~~ [题目大意]:N头牛排成一列1<=N<=5000.每头牛或者向前或者向后.为了让所有牛都 面向前方,农夫每次可以将K头连续的牛转向1<=K<=N,求操作的最少 次数M和对应的最小K. [思路]:由于交换区间翻转顺序对结果没影响,所以从左往右对于需要  翻转的牛进行反转,同时记录对该区间其他牛的影响即cal中的sum, 对于最后部分无法翻转的区间检查是否有反向牛,若有则方案失败.此题思想值得细细思考,常常有一种无限状态,化为有限状态. 代码:

POJ 3276 [Face The Right Way] 题解

题目大意 n头牛排成一行,有的牛面朝前,有的牛面朝后,每一次操作可以使连续的K头牛改变方向:求一个K,使得操作次数最少.输出K以及最少的操作次数.当有多个K满足条件时,输出最小的K. 题目分析 对一个区间来说,多次进行反转操作是没有意义的:另外反转的顺序对结果是没有影响的.所以这道题只需要对所有的可操作区间(即长度为K的区间)考虑是否需要反转. 考虑最左边的牛,当它面朝前时无需反转,当它面朝后时,就反转[1, K]区间一次.然后继续考虑第二头牛即可. 反转的时候不必每头牛都操作一次,只需用一个t

POJ 3276 Face The Right Way 开关问题

Face The Right Way Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4367   Accepted: 2025 Description Farmer John has arranged his N (1 ≤ N ≤ 5,000) cows in a row and many of them are facing forward, like good cows. Some of them are facin

POJ 3276 (开关问题)

题目链接: http://poj.org/problem?id=3276 题目大意:有一些牛,头要么朝前要么朝后,现在要求确定一个连续反转牛头的区间K,使得所有牛都朝前,且反转次数m尽可能小. 解题思路: 首先不要看错题意了,不是求最小K,不要二分.而且反转区间长度一定是K,小于K是不能反转的. 很明显得枚举K(1...n),并且有以下反转思路: ①从第一头牛开始,如果朝前,不管了.看下一头牛,如果朝后反转K长度区间.....一直扫到区间结束. ②第一趟结束后,如果不符合要求,继续重复①,直到所

poj 3276(反转)

传送门:Problem 3276 参考资料: [1]:挑战程序设计竞赛 先献上AC代码,题解晚上再补 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define mem(a,b) (memset(a,b,sizeof(a))) 6 const int maxn=5e3+30; 7 8 int N; 9 int dir[maxn];//0:forw

POJ 3276 反转问题

#include <cstdio> #include <iostream> #include <cstring> using namespace std; int n; int a[5005][2]; int flag[5005]; int fun(int x){ int sum = 0; int res = 0; memset(flag,0,sizeof(flag)); for(int i = 0;i+x <= n;i++){ if(sum%2 == 0 &am

Face The Right Way poj 3276 开关问题

Language: Default Face The Right Way Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 2811   Accepted: 1298 Description Farmer John has arranged his N (1 ≤ N ≤ 5,000) cows in a row and many of them are facing forward, like good cows. Some

Face The Right Way POJ - 3276 (开关问题)

Face The Right Way Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 6707   Accepted: 3123 Description Farmer John has arranged his N (1 ≤ N ≤ 5,000) cows in a row and many of them are facing forward, like good cows. Some of them are facin

Face The Right Way POJ 3276(反转)

原题 题目链接 题目分析 由题意可以知道,对于特定的k,同一个区间反转两次是无意义的,考虑能翻第一头牛的区间只有第一个区间,因此第一头牛直接决定是否要翻转第一个区间,然后问题规模减1,同理再对第二头牛进行同样的分析.这里的翻转操作可以用前缀和优化,设f[i]=1表示以第i头牛开始的区间被翻过,f[i]=0则表示没被翻过,则考虑第j头牛时,只需统计前k-1头牛的f值,如果和是奇数则表明第j头牛的朝向与原方向相反,否则相同,最后就可以用O(N)的复杂求求解一个k的最小步骤m,由于所有k都要试一遍,最