神奇的字符串
这个题晃眼一看,其实就可以知道,是一个比较简单的DP问题。我们设dp[i]表示一直到第 个字母为止 时所能得到的最长神奇字符串的长度。那么,如果第 个字母不在我们最终的最长神奇字符串当中,可以 得到dp[i]=dp[i-1],如果第 个字母在最长神奇字符串当中,那么,这个字母肯定会和最近一个和它相同的 字符进行匹配,记这个位置为pre[s[i]],因此,可以得到dp[i]=max(dp[i1],dp[pre[s[i]-1]]+2) 当然,我们再想一下,这个题就真的只能用DP来解决么?通过分析,我们可以知道,因为我们要得到尽 可能长的满足条件的字符串,那么,就需要尽可能多的添加满足条件的一对字符,因此,我们就可以利 用贪心的思想,给每一个找过的字母打上标记,如果我们发现当前字母在前面的标记中已经出现过了, 那么,我们就让答案+2,并清0所有标记,因为只有这样,我们才可以添加数量尽可能多的字母到字符 串当中。
#include <bits/stdc++.h>
using namespace std;
string s;
int mp[30];
int main() {
int t;
scanf("%d",&t);
while(t--){
memset(mp,0,sizeof(mp));
cin>>s;
int len=s.length(),m=0;
for(int i=0;i<len;i++){
if(mp[s[i]-'a']){
m+=2;
memset(mp,0,sizeof(mp));
}
else{
mp[s[i]-'a']=1;
}
}
printf("%d\n",len-m);
}
return 0;
}
海盗游戏
对于这个题,我们的最优解肯定是让自己尽可能多的取偶数,而让对手去取奇数。很明显,我们选取金 币的方案跟我们除以4得到的余数是几有关。 如果得到的余数是1或者是3,说明当前金币的数量是奇数,那没办法,我们只能从中取出1枚金币。 如果除以4的余数是2,很明显,这时我们如果取了一半,那么剩下的数就会是一个奇数,这时,我们就 取一半。 如果除以4的余数是0,这时,如果我们取了一半,那么,约翰也能取一半。如果这时我们只取1枚金 币,那么,约翰也只能取1枚金币,这样,我们下次取时,就可以保证我们取完一半之后,约翰也只能取 1枚金币(回到了除以4的余数是2的情况),因此,这时,我们也只取1枚金币(金币数等于4时除外)。 这样,我们就可以得到尽可能多的金币了。
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
scanf("%d", &t);
while (t--) {
long long a[2] = {}, u = 0, n;
scanf("%lld", &n);
while (n) {
if (n % 2 == 0 && ((n / 2) % 2 || n == 4))
a[u % 2] += n / 2, n /= 2;
else
n--, a[u % 2]++;
u++;
}
printf("%lld\n", a[0]);
}
}
五子棋盘
这个题是一个比较简单的模拟题。因为每一步棋的位置是确定的,那么,当我们添加一枚棋子以后,就 可以去判断,在当前状态之下,有没有人获得了整场比赛的胜利(即出现了连成5个的情况)。因为出现新 的胜利情况,肯定会和我们新下的这枚棋子有关,因此,我们只需要找与我们新下的这枚棋子成米字型 的这样一个区域的内容即可。
#include <bits/stdc++.h>
int n, a[20][20];
bool check(int x, int y, int z) {
a[x][y] = z;
int i = x + 1, j = y + 1, s = 1;
while (i < 16 && j < 16 && s < 5 && a[i][j] == z) ++s, ++i, ++j;
if (s == 5)
return 1;
i = x - 1,j=y-1;
while (i && j && s < 5 && a[i][j] == z) ++s, --i, --j;
if (s == 5)
return 1;
i = x + 1,j=y-1,s=1;
while (i < 16 && j && s < 5 && a[i][j] == z) ++s, ++i, --j;
if (s == 5)
return 1;
i = x - 1,j=y+1;
while (i && j < 16 && s < 5 && a[i][j] == z) ++s, --i, ++j;
if (s == 5)
return 1;
j = y - 1,s=1;
while (j && s < 5 && a[x][j] == z) ++s, --j;
if (s == 5)
return 1;
j = y + 1;
while (j < 16 && s < 5 && a[x][j] == z) ++s, ++j;
if (s == 5)
return 1;
i = x - 1,s=1;
while (i && s < 5 && a[i][y] == z) ++s, --i;
if (s == 5)
return 1;
i = x + 1;
while (i < 16 && s < 5 && a[i][y] == z) ++s, ++i;
if (s == 5)
return 1;
return 0;
}
int main() {
memset(a, -1, sizeof(a));
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
int x, y;
scanf("%d %d", &x, &y);
if (check(x, y, i & 1)) {
printf("%c %d", (i & 1 ? 'A' : 'B'), i);
return 0;
}
}
printf("Tie");
return 0;
}
能量石
对于这个题而言,解决问题的关键在于如果去处理这个能量消耗的问题。对于任意两个能量石x、y而 言,我们吃掉这两个石头能够得到的能量就为max(x.e+y.e-x.s*y.l , x.e+y.e-y.s*x.l),其中e表示初始能 量,l表示每秒损失的能量,s表示吃掉这个物品所消耗的时间。因此,我们就可以根据这一公式对所有 的石头进行排序,越靠前的石头对整个计算能量消失的比重就越大,就越先进行计算。然后,再将总的 时间看成背包的体积,每个物品的价值看成价值,对这个题进行01背包计算,最终就可以得到答案。
#include<bits/stdc++.h>
using namespace std;
int dp[10005];
struct node{
int s,e,l;
}arr[105];
bool cmp(node x,node y){
return x.s * y.l < y.s * x.l;
}
int main(){
int t,n,q=0;
scanf("%d",&t);
while(t--){
memset(dp,0,sizeof(dp));
q++;
int m=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&arr[i].s,&arr[i].e,&arr[i].l);
m+=arr[i].s;
}
sort(arr+1,arr+n+1,cmp);
for(int i=1;i<=n;i++){
for(int j=m;j>=arr[i].s;j--){
dp[j]=max(dp[j],dp[j-arr[i].s]+max(0,arr[i].e-arr[i].l*(j-arr[i].s)));
}
}
int ans=0;
for(int i=0;i<=m;i++){
ans=max(ans,dp[i]);
}
printf("Case #%d: %d\n",q, ans);
}
return 0;
}