贪心它来了!
A题:
题意:给定一个序列,每次在开头或结尾取出一个放在新序列的后面。要求新序列字典序最小(least lexicographic)。
讲题之前,先在这里科普一下什么叫做字典序。应该说是为自己科普(羞耻
把字母按照字典上的顺序进行排序,越在前的字母越小,字母串从前到后依次比较。
了解清楚这点以后,贪心策略似乎就变得很明了了。
只要每次把小的拿出来就可以了。
但是这里有一个坑,就是当前后字母一致如何抉择?
也很简单,就是取决于它的潜在字母,潜在字母越小,也就拉大了自己本身的潜力。
代码如下:
#include<iostream>
using namespace std;
const int N=2005;
char s[N];
bool llex(int f,int r){
while(s[f]==s[r]&&r>=f){
f++;
r--;
}
if(r<f||s[f]<s[r])return 1;
return 0;
}
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++)cin>>s[i];
int f=0,r=n-1;
int cnt=0;
while(r>=f){
if(llex(f,r)) cout<<s[f++];
else cout<<s[r--];
cnt++;
if(cnt==80)cnt=0,cout<<endl;
}
return 0;
}
B题:
这题最开始以为很简单,但后面wrong answer冷静下来以后发现自己并没有好的贪心策略,比较好的贪心策略又过于复杂,违背了贪心的初衷。
在搜索题解的过程中,我看到了一个新名词–并查集 优先队列
解题思路:将数据按照p而不是d进行排序,优先贪大的,默认位置选择截止日期,如果截止日期已被占,就往前找,直到找到一个空位或者不贪。
代码如下:
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int N=10005;
struct pdi{
int p,d;
}pd[N];
int a[N];
bool cmp(pdi x,pdi y){
return x.p>y.p;
}
int fd(int x){
if(a[x]==-1)return x;
else return fd(a[x]);
}
int main(){
int n;
while(cin>>n){
for(int i=0;i<n;i++)
cin>>pd[i].p>>pd[i].d;
sort(pd,pd+n,cmp);
memset(a,-1,sizeof(a));
int ans=0;
for(int i=0;i<n;i++){
int t=fd(pd[i].d);
if(t>0){
ans+=pd[i].p;
a[t]=t-1;
}
}
cout<<ans<<endl;
}
return 0;
}
D题:(Moo Volume)
这题我感觉算不上贪心,题意可以转化为每头牛怎样与其他所有牛进行交流。然后求和容量。。。
但是我在排序完了后,找了下规律,然后解题就变得非常非常简单,(但是记得int数据会爆)详情可以见代码。
#include<iostream>
#include<algorithm>
#include<string.h>
#define ll long long
using namespace std;
const int N=10005;
int a[N];
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
sort(a,a+n);
for(int i=n-1;i>0;i--)
a[i]-=a[i-1];
ll ans=0L;
for(int i=1;i<n;i++){
ll tmp;
tmp=2*i*(n-i);
ans+=tmp*a[i];
}
printf("%lld",ans);
return 0;
}
E题:
这题先将点转化为区间,然后在一堆区间中贪心。
代码如下:
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
const int N=1005;
struct range{
double s,e;
}a[N];
bool cmp(range x,range y){
return x.s<y.s;
}
int main(){
int n,d;
int k=1;
while(scanf("%d%d",&n,&d)&&n&&d){
int f=0;
for(int i=0;i<n;i++){
int x,y;
cin>>x>>y;
if(y>d)f=1;
double r=pow(d*d-y*y,0.5);
a[i].s=x-r;
a[i].e=x+r;
}
if(f){
printf("Case %d: -1\n",k++);
continue;
}
sort(a,a+n,cmp);
int ans=1;
double rs=a[0].s,re=a[0].e;
for(int i=1;i<n;i++){
rs=a[i].s;
if(a[i].s<=re)
re=min(re,a[i].e);
else{
ans++;
re=a[i].e;
}
}
printf("Case %d: %d\n",k++,ans);
}
return 0;
}
G题:
我感觉这题我做的非常暴力。。。
代码如下:
#include<iostream>
#define ll long long
using namespace std;
const int N=7;
int a[N];
bool is_exit(){
for(int i=1;i<N;i++)
if(a[i]!=0)return 0;
return 1;
}
int main(){
while(1){
scanf("%d%d%d%d%d%d",&a[1],&a[2],&a[3],&a[4],&a[5],&a[6]);
if(is_exit())break;
int ans=a[6]+a[5]+a[3]/4;
a[1]-=11*a[5];//处理a[5]
ans+=a[4];//处理a[4]
if(a[4]){
if(a[2]>=5*a[4])a[2]-=5*a[4];
else {
a[1]-=(5*a[4]-a[2])*4;
a[2]=0;
}
}
a[3]%=4;//处理a[3]
if(a[3]){
ans++;
a[1]-=8-a[3];
if(a[2]>=(4-a[3])*2-1){
a[2]-=(4-a[3])*2-1;
}
else{
a[1]-=((4-a[3])*2-1-a[2])*4;
a[2]=0;
}
}
ans+=a[2]/9;
a[2]%=9;
if(a[2]){
ans++;
a[1]-=36-a[2]*4;
}
if(a[1]>0){
ans+=a[1]/36;
if(a[1]%36!=0)
ans++;
}
cout<<ans<<endl;
}
return 0;
}
再放一个大佬写的优雅代码:
#include<cstdio>
#include<cstring>
int dir[4]={0,5,3,1};
int a[10];
int main(){
int i,sum,ans;
while(1){
sum=0;
for(i=1;i<7;++i){
scanf("%d",&a[i]);
sum+=a[i];
}
if(!sum) break;
ans=a[6]+a[5]+a[4]+(a[3]+3)/4;//计算边长为3 4 5 6的板子消耗量
int cnt_2=a[4]*5+dir[a[3]%4];//这个数组放置的很nice
if(a[2]>cnt_2)
ans+=(a[2]-cnt_2+8)/9;//当上面剩余的2*2板子量不足时,需要消耗新的板子
int cnt_1=ans*36-a[6]*36-a[5]*25-a[4]*16-a[3]*9-a[2]*4;
if(a[1]>cnt_1)//当上面剩余的1*1板子量不足时,需要消耗新的板子
ans+=(a[1]-cnt_1+35)/36;
printf("%d\n",ans);
}
return 0;
}
H题:
这题我想了好几个贪心策略,头与头比、尾与尾比、头与尾比。
然后都错了。。。
最开始我设置了一个win变量,然后考虑到相等这种情况需要特殊考虑,又设置了tie变量。最终都一一告败。
搜索题解发现,大佬们都是头尾同时比,并且每一次比较都有一个结果,想想也是,这种方式更符合贪心的风格。
代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1005;
int a[N],b[N];
int main(){
int n;
while(scanf("%d",&n)&&n){
for(int i=0;i<n;i++)cin>>a[i];
for(int i=0;i<n;i++)cin>>b[i];
sort(a,a+n);
sort(b,b+n);
int fa,fb,ra,rb;
fa=fb=0;
ra=rb=n-1;
int ans=0;
while(ra>=fa){
if(a[ra]>b[rb])ra--,rb--,ans+=200;
else if(a[ra]<b[rb])fa++,rb--,ans-=200;
else{
if(a[fa]>b[fb])fa++,fb++,ans+=200;
else if(a[fa]==b[fb]&&a[fa]<b[rb]||a[fa]<b[fb])
fa++,rb--,ans-=200;
else fa++,rb--;
}
}
cout<<ans<<endl;
}
return 0;
}
I题:
这题我认为挺有挑战性的!
这里只讨论2人以上的情况
- 假设有三人,那么用最快的人当船夫,所需时间为:a+b+c
- 假设有四人或以上,这里讨论只运过去速度最慢的两人。那么有两种方案需要进行比较并取较小值
方案一:总是拿最快的人做船夫。所需时间为:c+d+2a
方案二:最快的两人先过去,然后让其中一个把船开回来,然后最慢的两人再过去,再叫刚才第一次过去的剩下的那个人把船开回来。时间:a+2b+d
代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1005;
int a[N];
int cross_river(int x){
if(x==1)
return a[0];
if(x==2)return a[1];
if(x==3)return a[0]+a[1]+a[2];
int t1=2*a[0]+a[x-2]+a[x-1];
int t2=2*a[1]+a[0]+a[x-1];
return min(t1,t2)+cross_river(x-2);
}
int main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
sort(a,a+n);
cout<<cross_river(n)<<endl;
}
return 0;
}