编辑:请参阅此问题底部的解决方案(c ++)
我有一个编程竞赛,我一直在准备:)
我正在练习使用这些问题:
http://cemc.math.uwaterloo.ca/contests/computing/2009/stage2/day1.pdf
我在看问题B(“晚餐”)。
知道从哪里开始?除了天真的方法(即尝试所有排列)之外,我无法想到任何需要太长时间才能成为有效答案的方法。
顺便说一句,那里的语言是c ++和pascal我认为,但是我不在乎你用的是什么语言 - 我的意思是我想要的只是暗示我应该继续进行的方向,并简要解释一下沿着这个走。感觉我错过了一些明显的东西......当然,扩展的猜测非常受欢迎,但我只是想澄清一点,我不是在寻找一个完整的解决方案:)
你有一个长度为1-100的二进制字符串N(在问题中他们使用H和G而不是一个和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',然后您无法删除单个数字(您必须一次删除对或更大的组)。
我使用Mark Harrison's main algorithm和Paradigm's grouping idea并使用它们来创建下面的解决方案。如果您愿意,可以在official test cases上试用。
//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*/
}
您可以尝试的一些official test cases:
测试案例3
8 2
GHHGHGGH
答案:4
测试案例6
20 2
GGHGGHHGGGHHGHHGHHGG
答案:6
测试案例14
100 4
HGHGGGHGGGHGHGGGHHGHGGGHHGHHHGHGGHGGHHHGGHHGHHGHGHHHHGHHGGGHGGGHGHGHHGGGHGHGHGGGHHGHHHGHGGGHGGGHGHHH
答案:-1
说明:当没有正确答案时输出-1。
测试案例18
100 5
GHGGGGGHGGGGGGGHHHHGGGGGHGGHHHGGGGGHHHHGGHHHHHGGGGGGHHHHHHGGGHHHHHGHHGGHHHHHGGGHHGGHHGGGGGGHHHGGGGHH
答案:16
测试案例21
95 2
GGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGGHHGHGHGHGHGHGHGHGHGHGHGHGHGHGHGHG
答案:32
答案 0 :(得分:2)
执行以下步骤:
重复上述步骤。每一步都会合并一大组H +或G +。
最终你会有一个H +和一个G +(假设你有H和G两个开头)。删除那些。
(H +,G +表示x或更多H或G,其中x是一次可以删除的最小数。)
答案 1 :(得分:1)
如果你将连续的h或连续的g分别处理为1小时或1克,这个问题会变得更简单一些(它们的处理方式相同,因为ghhhhhg和ghg需要相同数量的删除)。
这将问题简化为一组h和g的交替。此时,删除2的所有较少计数将为您提供所需的操作数(加上“其他”剩余字母的1)。
算法可以在O(n)中完成:
在遍历集合之后,答案应该是2个计数器中较小的一个(删除较小字符数所需的删除次数)加1(对于“其他”字符,将留下)。 / p>
有更快的方法吗? (这实际上有用吗?;)
答案 2 :(得分:0)
这里有一个想法 - 没有失去一般性你可以用数字代替Gs和Hs的序列来重复该组中的字母数。
让我们来看看案例#2:GGHGGHHGGGHHGHHGHHGG
- 它变为2 1 2 2 3 2 1 2 1 2 2
删除一组字母并合并两个邻居正在移除一个数字并用它们的总和替换相邻的两个: 2 1 2 2 3 2 1 2 1 2 2 - &gt; 2 1 2 4 1 2 1 2 2 - &gt; 2 1 2 4 1 2 3 - &gt; 2 1 3 2 3 - &gt; 2 3 3 - &gt; 5 - &gt;零
如果你注意到,每当我们从中间(不是最左边或最右边)移除一个组时,长度减少2,所以所需的步数似乎在N / 2左右(其中N是长度我们开始的数字列表) - 扭曲是如果N是偶数,最后你将有2个元素的列表,将分两步清除(所以+1额外步骤)。
在我看来,最佳步数总是= trunc((N + 1)/ 2) - if 。因此,如果可以减少H-G链,那么这项工作似乎更像是一种发现 - 如果是,那么我们就知道最小步数。
思想?