原题:https://code.google.com/codejam/contest/2994486/dashboard
题意:Fegla给了Omar N组长度不超过一百且全部由小写字母组成的字符串,Omar可以对每个字符串进行两种操作,第一种操作就是添加一个相同的字符在某个字符旁边,第二种操作就是删除连续出现的字符中的一个。问通过这两种操作,Omar是否能使这N组字符串完全一样,若能则输出最小的操作数。
题解:关键就是要理解两种操作带来的影响,首先定义一个字符串的简单字符串:将原字符串中连续出现的字符只用一个字符表示。例如:aaabbbcc的简单字符串就是abc
- 易知执行两种操作后的字符串只会改变字符出现的频率,它的简单字符串是不会变化的,也即最终的字符串和所有N组字符串的简单字符串都是一样的。
- 假设N组中简单字符串的第i个字符出现的频率最小最大分别为mink,maxk,易知这N组中的第i个简单字符的最优代价频率不会超出[mink,maxk]的范围。
注意:要将字符串全部输入。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define _abs(x) (((x) > 0) ? (x) : (-1*(x)))
#define INF 0X7F7F7F7F
class solve
{
private:
int T;
int N;
char unit[105];
int unitLen;
vector<vector<int> > num;
public:
solve(int t):T(t)
{
scanf("%d",&N);
num.resize(N+1);
memset(unit,0,sizeof(unit));
printf("Case #%d: ",T);
if(processIn())
{
printf("Fegla Won\n");
}
else
{
printf("%d\n",calcMinStep());
}
}
int processIn();
int calcMinStep();
int checkM(int row,char flag);
int calcStep(int row,int sameNum);
};
int solve::calcStep(int row,int sameNum) //计算所有字符串中第row个简单字符变成频率为sameNum的代价
{
int re = 0;
for(int i = 0;i < N;i++)
{
re += _abs(num[i][row]-sameNum);
}
return re;
}
int solve::checkM(int row,char flag)
{
int re = INF;
if(flag) //最大值
{
re = 0;
}
for(int i = 0;i < N;i++)
{
if(flag)
{
re = max(num[i][row],re);
}
else
{
re = min(num[i][row],re);
}
}
return re;
}
int solve::calcMinStep()
{
int minStep = 0;
int unitMinStep;
int mink,maxk;
unitLen = strlen(unit);
for(int i = 0;i < unitLen;i++)
{
mink = checkM(i,0);
maxk = checkM(i,1);
unitMinStep = INF;
for(int k = mink;k <= maxk;k++) //最终相同字符串中unit[i]的频率只可能是mink到maxk中间的值
{
unitMinStep = min(unitMinStep,calcStep(i,k));
}
minStep += unitMinStep;
}
return minStep;
}
int solve::processIn()
{
char tmpStr[105];
char pre;
char IsWon = false;
int len;
int unitIndex;
int tmpNum;
getchar();
for(int i = 0;i < N;i++)
{
memset(tmpStr,0,sizeof(tmpStr));
gets(tmpStr);
if(IsWon) //Fegla已经赢了则不需要再处理字符串,但是字符串必须输入完毕
continue;
pre = 0;
unitIndex = 0;
tmpNum = 0;
len = strlen(tmpStr);
for(int j = 0;j < len;j++)
{
if(tmpStr[j] != pre) //第一次出现不同于前面邻接字符
{
pre = tmpStr[j];
if(tmpNum != 0)
{
num[i].push_back(tmpNum);
}
if(i == 0) //记录第一组的简单字符串
{
unit[unitIndex] = pre;
}
else
{
if(unit[unitIndex] != pre) //后面的简单字符串不能和第一组的对应则Fegla赢
{
IsWon = true;
}
}
++unitIndex;
tmpNum = 1;
}
else
{
tmpNum++;
}
}
num[i].push_back(tmpNum); //输入最后一个字符的频率
if(i != 0&&num[i].size() != num[i-1].size()) //后面的简单字符串长度不同于第一组则Fegla赢
{
IsWon = true;
}
}
return IsWon;
}
int main()
{
int t;
scanf("%d",&t);
for(int i = 1;i <= t;i++)
{
solve repeater(i);
}
return 0;
}