问题 A: 轩辕剑
题目描述
《轩辕剑外传:苍之涛》是大宇资讯旗下经典单机角色扮演游戏《轩辕剑》系列的第七部作品。
这是《轩辕剑》系列中Roth最喜欢的一部作品,主要的原因是这部作品中引入了"法宝"系统,游戏中玩家可以装备两件类型为"法宝"的装备,每种法宝可以在战斗中为装备者提供技能,并且法宝可以成长,成长之后技能将更加强力。
而在苍之涛中,Roth最喜欢的一个法宝叫做"...龙...",这个法宝的技能是对一个敌人造成随机伤害.随机的伤害值分为3位,每一位为一个0-9之间的数字,每个数字由玩家在旋转的轮盘中点击鼠标发出停止指令时指针悬停的数字位置确定,升级之后伤害值将变为4位,并且可以组织数字的位置.Roth在游戏的过程中经常用"...龙..."对敌人造成成吨的输出,现在请你帮助Roth确定对于已经选定好的4位数字,他可以造成的最大伤害是多少
输入
每组数据包含4个整数a,b,c,d;
0<=a,b,c,d<=9;
处理到文件尾
输出
输出可能的最大的伤害值(不要包含前导0)
样例输入
2 0 4 8 0 0 0 0 0 0 4 0
样例输出
8420 0 4000
题解:
这题贼长,大部分都是废话,其实贼简单。
题目的意思翻译成中文就是:输入n个数,给他们从大到小排个序。
注意多组测试数据(坑了我好几次)
源代码:
#include<bits/stdc++.h>
using namespace std;
int a[10];
int cmp(int a,int b)
{
return a>b;
}
int main()
{
while(cin>>a[1])
{
int n=4;
for(int i=2;i<=n;i++)cin>>a[i];
sort(a+1,a+1+n,cmp);
int ans=a[1]*1000+a[2]*100+a[3]*10+a[4]*1;
cout<<ans<<endl;
}
}
问题 B: 博弈
题目描述
小A和小B进行一场博弈,有一个串有n颗珠子的圆环,每次一个人可以取走连续的1颗或2颗(取走某个珠子之后,他旁边的两个珠子也不认为是连续的),两人轮流取,小A先手,两人都用最佳策略,问谁会赢。 谁拿到最后一个算赢
如果小A赢,输出A,否则输出B
输入
一个数n
输出
一个字母A或B表示谁赢
样例输入
【输入样例1】 1 【输入样例2】 2
样例输出
【输出样例1】 A 【输出样例2】 A
题解:
这是浙江理工大学2019年5月月赛的题
其实B是有稳赢策略滴,那就是看A怎么摆他只需对应着摆就行了。
源代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
if(n==1||n==2)cout<<"A"<<endl;
else cout<<"B"<<endl;
}
问题 C: 大水题
题目描述
现在给你N个正整数ai,每个数给出一“好数程度” gi(数值相同但位置不同的数之间可能有不同的好数程度)。对于在 i 位置的数,如果有一在j位置的数满足 j < i 且 ai=aj,则你可以将位于[j,i]闭区间内的序列评为“好序列”,然后获得∑gk(j≤k≤i)(此闭区间内“好数程度”之和)分数。
注意: 在所有情况下,每个数都只能被一个”好序列”包含(只能与其他相应数被评为”好序列”一次);在符合要求的情况下,”好序列”的评定次数不受限制,且通过不同”好序列”获得的分数可以累加。
输入
输入文件为“water.in”
第一行有一个正整数N。
接下来的一行有N个正整数ai,表示意义如上。
接下来的一行有N个正整数gi,表示ai的”好数程度”。
输出
输出文件为“water.out”
一个整数,你可以获得的最大分数(通过不同”好序列”获得的分数可以累加),保证答案在64位整型范围内。
样例输入
7 1 2 1 2 3 2 3 1 4 3 4 3 4 5
样例输出
23
提示
【数据范围】
对于50%的数据 2 ≤N≤ 100, 1 ≤ ai, gi ≤ 1000
对于100%的数据 2≤N≤300000,ai, gi为int范围整数
题解:
这题真的不水好吧......QAQ
一道dp题,其实随便一想就可以想到
一个简单的的dp,其实是暴力:
表示前i个数的最大分数
于是有
但是TLE,拿了50分
接着,我又经过一次又一次的修改,
得出了新的转移方程:
解释一下:
1.表示的是的数字和,所以会多算一部分,也就是
2.表示的是的最大分数
3.代表的是目前对于最优的那个点
但是,当本蒟蒻信心满满地提交时,却只得了30分 (QAQ*2)
最后,我终于得出了正确的转移方程
那就是:
1、dp[i]=max(dp[i-1],sum[i]-mp[a[i]]);
2、mp[a[i]]=min(mp[a[i]],sum[i-1]-dp[i-1]);
解释一下:
- dp[i]表示1~i的最大分数
- sum[i]表示的是1~i所有好数程度之和
- mp[a[i]]表示的是1~i中没有被选过的数之和
首先解释第一个式子:
翻译成中文就是:1~i的最大分数=max(1~i-1的最大分数,1~i的好数程度之和-没有被选过的数之和)
第二个式子:
因为使dp[i]最大,可以转换成没有被选过的数之和最小,或1~i之间的好数程度之和最大。
而sum[i]是定死的,所以只能把mp[a[i]]变得最小。
而第二个状态转移方程的意思就是使得mp[a[i]]成为1~i中最小的
下面为各个分数段的代码,并配上注释(以铭记那段悲惨的历史)
30分代码:
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define MOD 1000007
#define pi 3.14159
using namespace std;
const int MAXN=300005;
typedef long long ll;
int n;
int a[MAXN],bet[MAXN];
ll dp[MAXN],sum,ans=-1,g[MAXN];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)
{
cin>>g[i];
g[i]+=g[i-1];//1~i的和记录下来,可节省一重循环
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<i;j++)
{
if(a[i]==a[j])
{
dp[i]=dp[j-1]+g[i]-g[j-1];//转移方程
bet[i]=j-1;
if(bet[j]!=0&&dp[bet[j]]+g[i]-g[bet[j]]>dp[i])//比较
{
dp[i]=dp[bet[j]]+g[i]-g[bet[j]-1];//转移方程
bet[i]=bet[j];
}
}
ans=max(ans,dp[i]);//更新大值
}
}
cout<<ans<<endl;
}
50分代码:
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define mod 1000007
using namespace std;
const int MAXN=300005;
typedef long long ll;
int n;
int a[MAXN],g[MAXN];
ll dp[MAXN],sum,ans=-1;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>g[i];
mem(dp,0);
bool flag=false;
for(int i=1;i<=n;i++)
{
flag=false;
for(int j=1;j<i;j++)
{
sum=0;
if(a[i]==a[j])//如果有相匹配的
{
flag=true;
for(int k=j;k<=i;k++)sum+=g[k];//求和
dp[i]=max(dp[j-1]+sum,dp[i-1]);//状态转移方程
}
}
if(!flag)dp[i]=dp[i-1];//没有相匹配的
}
cout<<dp[n]<<endl;
}
AC代码:
#include<bits/stdc++.h>
#define mod 0x3f3f3f3f
using namespace std;
const int MAXN=300010;
long long a[MAXN],sum[MAXN],dp[MAXN],n;
map<long long,long long>mp;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)
{
cin>>sum[i];
sum[i]+=sum[i-1];//前缀和优化
}
for(int i=1;i<=n;i++)
{
if(!mp.count(a[i]))mp[a[i]]=mod;//先把mp[a[i]]变得很大
dp[i]=max(dp[i-1],sum[i]-mp[a[i]]);//转移方程1
mp[a[i]]=min(mp[a[i]],sum[i-1]-dp[i-1]); //转移方程2
}
cout<<dp[n]<<endl;
}
问题 D: 愤怒的牛
题目描述
农夫约翰建造了一座有n间牛舍的小屋,牛舍排在一条直线上,第i间牛舍在 x 的位置,但是约翰的m头牛对小屋很不满意,因此经常互相攻击。约翰为了防止牛之间互相伤害,因此决定把每头牛都放在离其它牛尽可能远的牛舍。也就是要最大化最近的两头牛之间的距离。
牛们并不喜欢这种布局,而且几头牛放在一个隔间里,它们就要发生争斗。为了不让牛互相伤害。John 决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是多少呢?
输入
第一行用空格分隔的两个整数n和m;
第二行为 n 个用空格隔开的整数,表示位置x。
输出
输出仅一个整数,表示最大的最小距离值。
样例输入
5 3 1 2 8 4 9
样例输出
3
样例解释
把牛放在 1, 4 ,8 这样最小距离是 3 。
题解:
二分答案
mid表示的是最长距离,每次check一下mid是否满足题目要求即可
代码内附有详尽注释
源代码:
#include<bits/stdc++.h>
using namespace std;
int a[100005];
int n,m;
bool check(int mid)
{
int cows=1;//开始时有一头牛
int sum=a[1]+mid;
for(int i=2;i<=n;i++)
{
if(a[i]<sum)continue;//如果a[i]不满足,continue
cows++;//牛的数量+1
sum=a[i]+mid;
}
if(cows>=m)return true;//若能装下M个小屋
else return false;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+1+n);
int mid,ans,y,x=0;
y=a[n]-a[1];
while(x<=y)
{
mid=(x+y)/2;
if(check(mid)==true)//若满足要求
{
x=mid+1;//扩大答案
ans=mid;//更新答案
}
else y=mid-1;//缩小答案
}
cout<<ans<<endl;
}