Algorithm 你将如何着手解决这个问题? 简介:

Algorithm 你将如何着手解决这个问题? 简介:,algorithm,language-agnostic,Algorithm,Language Agnostic,编辑:参见此问题底部的解决方案(c++) 我马上就要参加一个编程比赛了,我一直在准备:) 我正在练习使用这些问题: 我在看问题B(“晚餐”) 知道从哪里开始吗?除了幼稚的方法(即尝试所有排列)之外,我真的想不出任何东西,因为这需要很长时间才能成为一个有效的答案

编辑:参见此问题底部的解决方案(c++)

我马上就要参加一个编程比赛了,我一直在准备:)

我正在练习使用这些问题:

我在看问题B(“晚餐”)

知道从哪里开始吗?除了幼稚的方法(即尝试所有排列)之外,我真的想不出任何东西,因为这需要很长时间才能成为一个有效的答案

当然,扩展的猜测是非常受欢迎的,但我只是想澄清一下,我并不是在寻找一个完整的解决方案:)


问题的简短版本: 您有一个长度为1-100的二进制字符串N(在问题中,它们使用H和G,而不是1和0)。您必须以尽可能少的步骤从中删除所有数字。在每个步骤中,您可以删除任意数量的相邻数字,只要它们相同。也就是说,在每个步骤中,您可以删除任意数量的相邻G或任意数量的相邻H,但不能在一个步骤中删除H和G

例如:

HHHGHHGHH
示例的解决方案:

1. HHGGHH (remove middle Hs)
2. HHHH (remove middle Gs)
3. Done (remove Hs)
   -->Would return '3' as the answer.
请注意,在删除相邻组时,也可以限制相邻组的大小。例如,它可能会说“2”,然后您不能删除单个数字(您必须一次删除对或更大的组)


解决方案 我用它们创建了下面的解决方案。如果你愿意,你可以在网上试试

//B.cpp
//include debug messages?
#define DEBUG false


#include <iostream>
#include <stdio.h>
#include <vector>

using namespace std;

#define FOR(i,n) for (int i=0;i<n;i++)
#define FROM(i,s,n) for (int i=s;i<n;i++)
#define H 'H'
#define G 'G'

class String{
public:
    int num;
    char type;
    String(){
        type=H;
        num=0;
    }
    String(char type){
        this->type=type;
        num=1;
    }
};

//n is the number of bits originally in the line
//k is the minimum number of people you can remove at a time
//moves is the counter used to determine how many moves we've made so far
int n, k, moves;
int main(){

    /*Input from File*/
    scanf("%d %d",&n,&k);
    char * buffer = new char[200];
    scanf("%s",buffer);

    /*Process input into a vector*/
    //the 'line' is a vector of 'String's (essentially contigious groups of identical 'bits')
    vector<String> line;
    line.push_back(String());
    FOR(i,n){

        //if the last String is of the correct type, simply increment its count
        if (line.back().type==buffer[i])
            line.back().num++;

        //if the last String is of the wrong type but has a 0 count, correct its type and set its count to 1
        else if (line.back().num==0){
            line.back().type=buffer[i];
            line.back().num=1;
        }

        //otherwise this is the beginning of a new group, so create the new group at the back with the correct type, and a count of 1
        else{
            line.push_back(String(buffer[i]));
        }

    }

    /*Geedily remove groups until there are at most two groups left*/
    moves=0;
    int I;//the position of the best group to remove
    int bestNum;//the size of the newly connected group the removal of group I will create
    while (line.size()>2){

        /*START DEBUG*/
        if (DEBUG){
            cout<<"\n"<<moves<<"\n----\n";
            FOR(i,line.size())
                printf("%d %c \n",line[i].num,line[i].type);
            cout<<"----\n";
        }
        /*END DEBUG*/

        I=1;
        bestNum=-1;
        FROM(i,1,line.size()-1){
            if (line[i-1].num+line[i+1].num>bestNum && line[i].num>=k){
                bestNum=line[i-1].num+line[i+1].num;
                I=i;
            }
        }
        //remove the chosen group, thus merging the two adjacent groups
        line[I-1].num+=line[I+1].num;
        line.erase(line.begin()+I);
            line.erase(line.begin()+I);

            //we just performed a move
        moves++;
    }

    /*START DEBUG*/
    if (DEBUG){
        cout<<"\n"<<moves<<"\n----\n";
        FOR(i,line.size())
            printf("%d %c \n",line[i].num,line[i].type);
        cout<<"----\n";
        cout<<"\n\nFinal Answer: ";
    }
    /*END DEBUG*/


    /*Attempt the removal of the last two groups, and output the final result*/
    if (line.size()==2 && line[0].num>=k && line[1].num>=k)
        cout<<moves+2;//success
    else if (line.size()==1 && line[0].num>=k)
        cout<<moves+1;//success
    else
        cout<<-1;//not everyone could dine.

    /*START DEBUG*/
    if (DEBUG){
        cout<<" moves.";
    }
    /*END DEBUG*/
}
答复:4

  • 测试用例6

    20 2
    GGHGGHHGGGHHGHHGHHGG
    
    答复:6

  • 测试用例14

    100 4
    HGHGGGHGGGHGHGGGHHGHGGGHHGHHHGHGGHGGHHHGGHHGHHGHGHHHHGHHGGGHGGGHGHGHHGGGHGHGHGGGHHGHHHGHGGGHGGGHGHHH
    
    答复:-1

    说明:-1在没有正确答案时输出

  • 测试用例18

    100 5
    GHGGGGGHGGGGGGGHHHHGGGGGHGGHHHGGGGGHHHHGGHHHHHGGGGGGHHHHHHGGGHHHHHGHHGGHHHHHGGGHHGGHHGGGGGGHHHGGGGHH
    
    答复:16

  • 测试用例21

    95 2
    GGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGHGHGHGHGHGHGHGHGHGHGHGHGHGHGHG
    
    答复:32


  • 如果将连续h或连续g分别视为1小时或1克,则此问题会变得更简单(它们的处理方式相同,因为ghhhhhg和ghg需要相同的清除次数)

    这将问题简化为一组h和g的交替。在这一点上,删除2的所有较小计数将得到所需的操作数(对于“其他”剩余字母加1)

    算法可以在O(n)中完成:

    • 为h和g保留一个计数器
    • 从第一个字符开始,将相应的计数器递增1
    • 转到下一个字符,如果它与上一个字符相同,则继续下一个字符
    • 如果不同,则增加新字符的计数器
    • 继续相同的过程,每次角色从上一个更改时递增相应的计数器
    遍历集合后,答案应该是2个计数器中较小的一个(删除较小的字符所需的删除量),加上1(对于“其他”字符,将保留)

    有没有更快的办法?(这真的有效吗?;)

    执行以下步骤:

    • 寻找一个模式,如H+G+H+。移除留下聚结H+的G+,其中一个或多个原始H+H+由于长度限制无法移除。否则,移除最长的聚结H+
    • 同样,对于G+H+G+
    重复上述步骤。每一步都将合并一组更大的H+或G+

    最终你会有一个H+和一个G+(假设你一开始就有H和G)。把那些拿走


    (H+,G+表示x个或多个H或G,其中x是一次可以删除的最小数字。)

    好吧,这里有一个想法-没有通用性,您可以用重复组中字母计数的数字替换Gs和Hs序列

    让我们以案例2为例:
    gghgghgghgghgg
    -它变为
    21232122

    删除一组字母并合并两个相邻字母就是删除一个数字并用其和替换两个相邻字母: 2122321232->2124121232->241233->212322332335->0

    如果您注意到,每次我们从中间(不是最左边或最右边)移除一个组时,长度都会减少2,因此所需的步数似乎在N/2左右(其中N是我们开始的数字列表的长度)-如果N是偶数,在最后,您将有2个元素的列表,这些元素将在2个步骤中清除(因此+1个额外步骤)

    因此,在我看来,最佳步数始终是=trunc((N+1)/2)
    ,如果有可能的话。因此,这项工作似乎更多的是寻找是否有可能减少H-G链——如果有,那么我们知道最小步数


    想法?

    不,不幸的是,除了按描述执行移除外,你不能移动任何东西。对列表进行排序会使答案始终为1或2,我认为这不是预期的效果。肩膀放低,膝盖放低。这是相邻对的大小让这件事很有趣。如果固定为2个或更多,那么@Paradigm的答案是最好的,但是分组意味着你需要按正确的顺序获得足够多的字母才能删除它们。这可能不起作用。他们说Hs和Gs的最大数量是100个,所以我希望肯定会有比O(n)效率更低的东西。我知道你在这里想干什么。但在更复杂的情况下,这是行不通的。谢谢你!事实上,尽管你把它们当作“集合”的想法很聪明。+1-最后的解决方案见我更新的问题。您的分组id
    95 2
    GGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGHGHGHGHGHGHGHGHGHGHGHGHGHGHGHG