CUGBACM Codeforces Tranning 2 解题报告

来源:互联网 发布:知乎精彩回答合集 编辑:程序博客网 时间:2024/05/07 20:58

比赛链接:点击打开链接

首先嘲讽一下我那渣到没朋友的英文。。。我tm四级是怎么过的。。。。不怕单词没见过,就怕记错意思还以为自己叼到没朋友啊。。。。A B两题纯水题,因为理解错了题意,一直WA,一直调试。。。结果时间不够了。。。C题看都没时间看。。。其实C题也很水,读完题后思路立马就有了,敲题也不过半个小时,但是还是因为眼高手低wa了两发,罪过罪过。

一句话:我还是太弱!!!各种弱!!!

A B题纯水题,我自己代码也比较挫,就不贴了。

C题。这一次,我终于可以说:这其实就是一道水dp!!!!分分钟搞定 (我是不是又浪了。。。。)

题意要求严格递增,因此就要注意相等的情况,只要加一个约束就好。这道题的方法是先贪心,后dp。先对w从小到大排序,这样w就按照从小到大排了。剩下需要做的就是对h求一个最长上升子序列了,同时要记录每一个序列的前一个元素,最后要输出。输出的时候,没想到什么简洁的方法,直接写了一个dfs。

AC代码

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<cmath>#include<iomanip>#include<algorithm>#include<queue>#include<map>#include<set>using namespace std;#define pi acos(-1.0)#define eps 1e-6typedef long long ll ;const int inf = 1<<29;struct node{    int w,h,id;} a[5005];int dp[5005],pre[5005];bool cmp(node a,node b){    if(a.w==b.w)        return a.h<b.h;    return a.w<b.w;}void print_id(int num){    if(num==-1)        return ;    print_id(pre[num]);    printf("%d ",a[num].id);    return ;}int main(){    int i,j,n,w,h,tw,th,cnt=0,mx,flag;    scanf("%d%d%d",&n,&w,&h);    for(i=1; i<=n; i++)    {        scanf("%d%d",&tw,&th);        if(tw>w && th>h)        {            a[cnt].w=tw,a[cnt].h=th,a[cnt++].id=i;        }    }    if(cnt==0)    {        printf("0\n");        return 0;    }    memset(pre,-1,sizeof(pre));    sort(a,a+cnt,cmp);    mx=dp[0]=1,flag=0;    for(i=1; i<cnt; i++)    {        dp[i]=1;        for(j=0; j<i; j++)        {            if(a[j].w<a[i].w && a[j].h<a[i].h)            {                if(dp[i]<dp[j]+1)                {                    dp[i]=dp[j]+1;                    pre[i]=j;                }            }        }        if(mx<dp[i])        {            mx=dp[i];            flag=i;        }    }    printf("%d\n",mx);    print_id(flag);    return 0;}

D题 

给定一个大卡车的容量 ,用大卡车来装小摩托,小摩托有体积,分为1,2两种,但是所承载的体积不同。求大卡车上的小摩托所承载的体积最大值。

这题最先容易想到的是背包,但也是被最先否定的。因为n是10^9太大了,数组存不了那么大。但是这题有一个特点,小摩托只有1,2两种体积。因此可以将两种小摩托分开,对每一部分取贪心。然后将两种组合,o(n)扫一遍即可。

AC代码

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<cmath>#include<iomanip>#include<algorithm>#include<queue>#include<map>#include<set>using namespace std;#define pi acos(-1.0)#define eps 1e-8typedef long long ll ;const int inf = 1<<29;struct node{    int w,id;};node one[105005],two[105005];bool cmp(node a,node b){    return a.w>b.w;}int sum1[105005],sum2[105005];int res[305005];int main(){    int n,v,i,a,b,o_cnt=1,t_cnt=1;    scanf("%d%d",&n,&v);    sum1[0]=sum2[0]=0;    for(i=1; i<=n; i++)    {        scanf("%d%d",&a,&b);        if(a==1)        {            one[o_cnt].w=b;            one[o_cnt].id=i;            o_cnt++;        }        else if(a==2)        {            two[t_cnt].w=b;            two[t_cnt].id=i;            t_cnt++;        }    }    sort(one+1,one+o_cnt,cmp);    sort(two+1,two+t_cnt,cmp);    for(i=1; i<o_cnt; i++) sum1[i]=sum1[i-1]+one[i].w;    for(i=1; i<t_cnt; i++) sum2[i]=sum2[i-1]+two[i].w;    int ans=-1,o_ans=0,t_ans=0;    for(i=0; i<o_cnt; i++)    {        if(i>v) break;        int tmp=sum1[i]+sum2[min((v-i)/2,t_cnt-1)];        if(tmp>ans)        {            ans=tmp;            o_ans=i;            t_ans=min((v-i)/2,t_cnt-1);        }    }    // printf("%d %d\n",o_ans,t_ans);    if(ans==-1)    {        printf("0\n");        return 0;    }    printf("%d\n",ans);    int cnt = 0;    for(i=1; i<=o_ans; i++) res[cnt++] = one[i].id;    for(i=1; i<=t_ans; i++) res[cnt++] = two[i].id;    //cout<<cnt<<endl;    for(i=0; i<cnt; i++)        printf("%d%c", res[i], i == cnt-1? '\n' : ' ');    puts("");    return 0;}


E题 给定三个圆,求一点(x,y)使得这一点对三个圆的视角相同,所谓视角,就是从该点做圆的两条切线,夹角即为视角。

一种想法:设d为点到圆心距离,视角的一般为 asin(d/r),要求视角相同,即d/r为定值。

(x-x1)^2+(y-y1)^2=(r*d1)^2
(x-x2)^2+(y-y2)^2=(r*d2)^2
(x-x3)^2+(y-y3)^2=(r*d3)^2.

但是这个三元二次方程组不会解。。。。。网上有一种模拟退火方法。不怎么懂。。。。

按琦神的方法,这个方程组可以理论AC:对于任意两个方程,若d1=d2,则化简之后是求直线交点。否则,是求圆的交点。orz。。。。解方程都用上数形结合了。不过因为没有好的模板,还是不好处理。。。。

 

0 0