做这题首先要明白gary code(格雷码)的算法
假设有二进制 10010 对应的格雷码就是它本身与它左移一位后的二进制异或
10010
10010
11011
题目中每个位置都给了一个价值a 给的二进制由‘0’ ‘1’ ‘?’组成 ‘?’可以是1也可是0 格雷码为1的位置所对应的价值加和为最终价值 输出最大的最终价值
根据二进制转换格雷码的方法可知 当前位置与前一位置异或结果为当前位置格雷码
推理可知连续的’?’对结果起决定作用 可知有如下几种情况
‘0’ 奇数个’?’ ‘1’
‘1’ 奇数个’?’ ‘0’
0 奇数 0
1 奇数 1
0 偶数 1
1 偶数 0
0 偶数 0
1 偶数 1
而进一步研究可知
1 偶数 0
0 偶数 1
0 奇数 0
1 奇数 1
一定有让所有’?’下价值算入的情况
其余一定要去掉一个数 当然选其间的最小价值
另外有两个特例 如果????连续到右时 无论奇偶期间价值均可选
开头为?时 该段’?’左端其实为’0’(界外 但计算格雷码时要与第一位异或)
总结上面的情况即可写出
代码如下:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define INF 0x3f3f3f3f
using namespace std;
char str[233333];
int a[233333];
int main()
{
int t,n,i,len,mm,z = 0,sum;
char l;
scanf("%d",&t);
while(t--)
{
scanf("%s",str);
n = strlen(str);
sum = len = 0;
l = '0';
mm = INF;
for(i = 0; i < n; ++i)
{
scanf("%d",&a[i]);
sum += a[i];
mm = min(mm,a[i]);
if(str[i] == '?')
{
len++;
}
else
{
if(len%2)
{
if(l != str[i]) sum -= mm;
}
else
{
if(l == str[i]) sum -= mm;
}
len = 0;
mm = INF;
l = str[i];
}
}
printf("Case #%d: %d\n",++z,sum);
}
return 0;
}
刚刚逛大牛们的博客时发现这题用dp也可以 并且更好理解
列个if大家就明白了
scanf("%d",&a);
dp[0][0] = 0;
if(str[0] != '0') dp[0][1] = a;//初始化最左端价值
for(i = 1; str[i]; ++i)
{
scanf("%d",&a);
if(str[i] == '0')
{
if(str[i-1] == '0') dp[i][0] = dp[i-1][0];
else if(str[i-1] == '1') dp[i][0] = dp[i-1][1] + a;
else if(str[i-1] == '?') dp[i][0] = max(dp[i-1][0],dp[i-1][1]+a);
}
else if(str[i] == '1'){同上}
else if(str[i] == '?')
{
if(str[i-1] == '0')
{
dp[i][0] = dp[i-1][0];
dp[i][1] = dp[i-1][0]+a;
}
else if(str[i-1] == '1')
{
dp[i][0] = dp[i-1][1] + a;
dp[i][1] = dp[i-1][1];
}
else if(str[i-1] == '?')
{
dp[i][0] = max(dp[i-1][0],dp[i-1][1]+a);
dp[i][1] = max(dp[i-1][1],dp[i-1][0]+a);
}
}
}
上代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
char str[233333];
int dp[233333][2];
int main()
{
int t,k = 0,i,a;
scanf("%d",&t);
while(++k,t--)
{
scanf("%s",str);
scanf("%d",&a);
dp[0][0] = dp[0][1] = 0;
if(str[0]-'0') dp[0][1] = a;
for(i = 1; str[i]; ++i)
{
scanf("%d",&a);
if(str[i] != '?')
{
if(str[i-1] == '?')
{
dp[i][str[i]-'0'] = max(dp[i-1][str[i]-'0'],dp[i-1][(str[i]-'0'+1)%2]+a);
}
else dp[i][str[i]-'0'] = dp[i-1][str[i-1]-'0'] + ((str[i] == str[i-1])? 0: a);
}
else//1跟0的我给写一块。。发现跟分开写代码量差不多。。分开写还好理解点……
{
if(str[i-1] == '?')
{
dp[i][0] = max(dp[i-1][0],dp[i-1][1]+a);
dp[i][1] = max(dp[i-1][1],dp[i-1][0]+a);
}
else
{
dp[i][0] = dp[i-1][str[i-1]-'0'] +((str[i-1]-'0')? a: 0);
dp[i][1] = dp[i-1][str[i-1]-'0'] + ((str[i-1]-'0')? 0: a);
}
}
}
if(str[i-1]!='?') printf("Case #%d: %d\n",k,dp[i-1][str[i-1]-'0']);
else printf("Case #%d: %d\n",k,max(dp[i-1][0],dp[i-1][1]));
}
return 0;
}