12月补题记(4)

贪心它来了!
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人以上的情况

  1. 假设有三人,那么用最快的人当船夫,所需时间为:a+b+c
  2. 假设有四人或以上,这里讨论只运过去速度最慢的两人。那么有两种方案需要进行比较并取较小值
    方案一:总是拿最快的人做船夫。所需时间为:c+d+2a
    方案二:最快的两人先过去,然后让其中一个把船开回来,然后最慢的两人再过去,再叫刚才第一次过去的剩下的那个人把船开回来。时间:a+2
    b+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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值