比赛传送门:User Login (hdu.edu.cn)
1003Dota2 Pro Circuit
题目大意:给定每个队伍之前的得分ai,下一场可能得到的分数bi,求每一个队伍最好的与最坏的名次。
思路:贪心
分析:对于任意的一个队伍而言,它本身最好的得分是现在的得分加上下一场第一名的得分,最坏的情况是现在的得分加上下一场最后一名的得分。 要计算名次我们可以将其转换为分数,最好的名次,就是使比他本身分数更低的队伍尽可能多,最坏的同理,使比他分数高的队伍尽可能多。详情见代码注释。
代码:
#include<bits/stdc++.h>
using namespace std;
int t,n;
struct node{
long long num,val;
}poi[5001];
int ans[5001][2];
long long b[5001];
//从小到大进行排序
bool cmp(node a1,node a2)
{
return a1.val<a2.val;
}
//输入并排序
void init()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%lld",&poi[i].val);
poi[i].num=i;
}
for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
sort(poi+1,poi+1+n,cmp);
return;
}
//求最好的名次 尽量使得其他队伍分数更低 最大的分数给目前最小的队伍
int maxn(int k)
{ //求解第k个队伍 最好得分是加上最大值
int N=poi[k].val+b[1],cnt=0;
//双指针 p指向排序后的a数组 q指向b数组
int p=1;
int q=1;
//当循环没有走完的时候
while(p<=n && q<=n){
if(p==k){
//第k个不必再加 跳过
p++;
continue;
}
//目前队伍得分比k小的时候 cnt++记录一共有多少更小的
if(poi[p].val+b[q]<=N){
p++;
cnt++;
}
q++;
}
//减去比自己分数少的队伍就是自己的名次
return n-cnt;
}
//求最差的名次
int minn(int k)
{
int d=1,N=poi[k].val+b[n],ans=0;
//ans超过当前的个数,N为队伍最少可以得分
for(int i=1;i<=n;i++){
if(i==k) continue;
if(poi[i].val>N){
//如果不用加分数就已经超过一定在它前面
ans++;
continue;
}
else
{ //加上更大 消耗一个d,d++
if(poi[i].val+b[d]>N){
ans++;
d++;
}
}
}
return ans+1;
}
int main() // AC
{
scanf("%d",&t);
while(t>0)
{
t--;
init();
for(int i=1;i<=n;i++){
int number=poi[i].num;
ans[number][0]=maxn(i);
ans[number][1]=minn(i);
}
for(int i=1;i<=n;i++){
printf("%d %d\n",ans[i][0],ans[i][1]);
}
}
return 0;
}
1002Just another board game
题目大意:两个人下棋,棋盘的每个格子上有一个数字。先手可以同一行左右移动(可以不动),要让停留的数字最大;后手可以同一列上下移动(可以不动),要让停留的数字最小。轮到任何一方走时,他可以选择直接结束游戏,走k步游戏也会结束。
给出一个棋盘和一个固定大小k求输出的值。
分析:当k=1,只能有第一个操作一次,结果即为第一行上最大的数。
当k为偶数,当k>2时的结果是等价与k=2的。以k=4为例,每个人都可以选择随时停止游戏,当k=4对先手更为有利时,后手会选择在k=2时直接结束游戏,反之,对后手更有利的时候,先手会在k=1时候结束游戏,因此,只需要考虑k=2的时候即可。 当k=2,假设初始位置为s[x][0];那么有两种情况,要么,都不操作,结果即为s[x][0],要么先手会选择棋盘上列最小值最大的一列(后手必定会操作使得结果停留在那一列的最小值上),因此需要输出结果max(s[x][0],max(每列最小值))。
当k为奇数的时候,等价于k=3,原理同上。当k=3的时候,有三种情况,先手直接结束,后手直接结束和走完三步(最后一个人直接结束一定没有走完三步更优,因此一定不会有这种情况发生),假设开始的位置为s[x][0],直接结束为s[x][0],后手直接结束会停在这一行的最大值上,否则,会停在每一行的最大值最小的那个数上(后手没有结束游戏的时候,最后先手一定会操作,所以一定会停在一个行最大值上,因此后手必定会选择行最大值最小的一列),我们发现第二种和第三种情况都会停在行最大值上,因此第三种无疑是最优解决方案,直接考虑第一种与第三种即可,即输出max(s[x][0],max(行最大值最小的列的行最大值))
代码:
#include<bits/stdc++.h>
#define ll long long
#define cer(x) cerr<<(#x)<<" = "<<(x)<<'\n'
using namespace std;
const int N=1e6+10;
ll n,m,k;
ll min1[N];
ll maxline[N];
vector<ll> v[N];
void init(){
for(int i=1;i<=n;i++){
v[i].clear();
}
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
int t; cin>>t;
while(t--){
cin>>n>>m>>k;
init();
ll temp;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>temp;
v[i].push_back(temp);
}
}
ll max1=0;
for(int i=0;i<m;i++){
max1=max(max1,v[1][i]);
}
for(int i=1;i<=m;i++){
min1[i]=1e9+10; // inf
}
for(int i=0;i<m;i++){
for(int j=1;j<=n;j++){
min1[i+1]=min(min1[i+1],v[j][i]);
}
}
for(int i=1;i<=m;i++){
maxline[i]=0;
}
for(int i=1;i<=n;i++){
for(int j=0;j<m;j++){
maxline[i]=max(maxline[i],v[i][j]);
}
}
if(k==1){
cout<<max1<<endl;
continue;
}
// k>=2
if(k%2==0){ // 相当于k==2
ll maxx=0;
for(int i=2;i<=m;i++){
maxx=max(maxx,min1[i]);
}
cout<<max(v[1][0],maxx)<<endl;
}
else{ // k>=3,奇数
ll minn=1e9+10;
for(int i=1;i<=n;i++){
minn=min(minn,maxline[i]);
}
cout<<max(v[1][0],minn)<<endl;
}
}
return 0;
}