前言:
受水平限制,仅能在比赛及其结束后写出前三题。
题目:
SDOI
- 思路:就是纯模拟,只不过略为恶心。
- 代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=105;
struct node
{
string name;
int sex,r1,r2;
double ave;
bool operator < (const node &a) const
{
return ave>a.ave;
}
};
int t,n,m,high1,high2;
bool flag;
node man[maxn];
void work()
{
cout<<"The member list of Shandong team is as follows:"<<endl;
sort(man+1,man+n+1);
if (!flag)
{
for (int i=1;i<=m;++i)
cout<<man[i].name<<endl;
}
else
{
bool in=0;
int p=0;
for (int i=1;i<=m-1;++i)
{
cout<<man[i].name<<endl;
if (man[i].sex==1)
{
in=1;
}
}
if (in==1)
{
cout<<man[m].name<<endl;
return;
}
else
{
int p=m-1;
while (true)
{
p++;
if (man[p].sex==1)
{
cout<<man[p].name<<endl;
return;
}
}
}
}
}
void init()
{
scanf("%d",&t);
while (t--)
{
flag=0;
high1=high2=0;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
{
cin>>man[i].name;
string s;
cin>>s;
if (s=="male")
man[i].sex=0;
else
{
man[i].sex=1;
flag=1;
}
cin>>man[i].r1>>man[i].r2;
high1=max(high1,man[i].r1);
high2=max(high2,man[i].r2);
}
for (int i=1;i<=n;++i)
{
man[i].ave=((double)man[i].r1*(300.0/(double)high1))*0.3+((double)man[i].r2*(300.0/(double)high2))*0.7;
}
work();
}
}
int main()
{
init();
return 0;
}
Reorder The Books
- 思路:可以发现,一定先操作大的数,再操作小的数。因为假如先把小的数放前面去了,再把大的数放前面去,小的数就又在大的数后面了,小的数必定还得再操作一次,然而操作两次是不划算的。同时可以发现最大的数
n
是不用操作的(其他数操作好了,数
n 自然就在最后面了)。 于是我们先找到最大的数 n 的位置,从这个位置往前找,直到找到(n−1) 。假如找到头也没找到 (n−1) ,那么数 (n−1) 需要操作,而一旦操作了 (n−1) ,根据前面发现,总共就需要 (n−1) 次操作了;假如找到了 (n−1) ,那么数 (n−1) 也不需要操作(和数 n 不需要操作一个道理)。 同理,我们接着从(n−1) 的位置往前找 (n−2) ,再从 (n−2) 的位置往前找 (n−3) …假如数 k 找不到了,那么就至少需要k 次操作。这种做法的复杂度是 O(n) 的。 - 代码如下:
- 思路:可以发现,一定先操作大的数,再操作小的数。因为假如先把小的数放前面去了,再把大的数放前面去,小的数就又在大的数后面了,小的数必定还得再操作一次,然而操作两次是不划算的。同时可以发现最大的数
n
是不用操作的(其他数操作好了,数
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=100;
int t,n,a[maxn];
int main()
{
scanf("%d",&t);
while (t--)
{
scanf("%d",&n);
int pos;
bool f=0;
for (int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
if (a[i]==n)
pos=i;
}
for (int k=n-1;k>=0;--k)
{
if (k==0)
{
f=1;
break;
}
bool flag=0;
for (int i=pos;i>=1;--i)
{
if (a[i]==k)
{
flag=1;
pos=i;
break;
}
}
if (flag)
continue;
else
{
printf("%d\n",k);
break;
}
}
if (f)
{
printf("0\n");
}
}
return 0;
}
The Highest Mark
- 思路:贪心+动态规划。
- 首先发现,交换相邻两个题目的做题顺序对其它题目是没有影响的。设两者从t=0时刻开始做,两题编号为1、2。先1后2,得分
score1=a[1]−b[1]∗c[1]+a[2]−b[2]∗(c[1]+c[2])
,
先2后1,得分 score2=a[2]−b[2]∗c[2]+a[1]−b[1]∗(c[1]+c[2]) ,
若score1>score2,会有b[1]c[2]>b[2]c[1]即b[1]/c[1]>b[2]/c[2],那么以b[i]/c[i]为关键字由大到小进行排序(因为01背包对物品考虑顺序有先后)即可。 最后就是01背包的事了,设f[j]代表用了时间j能取的最大得分,则有f[j]=max(f[j],f[j-c[i]]+a[i]-j*b[i])。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
int a,b,c;
double d;
bool operator < (const node &a) const
{
return d>a.d;
}
};
const int maxn=1005;
int n,t,T;
node pro[maxn];
int f[3005];
void dp()
{
memset(f,0,sizeof(f));
sort(pro+1,pro+n+1);
for (int i=1;i<=n;++i)
for (int j=t;j>=pro[i].c;--j)
f[j]=max(f[j],f[j-pro[i].c]+pro[i].a-j*pro[i].b);
int ans=0;
for (int i=0;i<=t;++i)
ans=max(ans,f[i]);
printf("%d\n",ans);
}
void init()
{
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&t);
for (int i=1;i<=n;++i)
{
scanf("%d%d%d",&pro[i].a,&pro[i].b,&pro[i].c);
pro[i].d=(double)pro[i].b/(double)pro[i].c;
}
dp();
}
}
int main()
{
init();
return 0;
}