suac 2012新生賽 F切水果


推薦技術公眾號:不愛睡覺的大豬

 

題意:

描述

   Lrc是校隊里面的總所周知的全才王,他不僅是一個excelent acmer,也不僅是一個chess master,更是一個crazy game player。
切水果,正是他最喜歡玩的手機游戲之一。為了避免有人沒玩過,下面介紹一下Lrc是怎么玩這個游戲的~-~
1) 整個屏幕是一個笛卡爾坐標系。
2) 在某個時刻,屏幕上會出現灰常多的水果,西瓜、草莓神馬的,當然還有一種,炸彈。
3) 每個時刻用手指在屏幕上划過,切中水果可以得到一定得分數,切到炸彈就要扣分。
4) 每個時刻最多只能切一刀,至於一刀切多少個可以自由控制。
5) 第t時刻沒有被切的水果,將不會出現在下一個時刻。
6) 由於Lrc 骨骼奇精,所以手指在屏幕上只能划直線(線段),也就是說只有在同一直線上的水果他才能在同一時間內切到。(友情提醒,直線不僅是斜的,也可以是水平和垂直的哦)。
   由於Lrc比較神,每次他總能得到最高分。嘿嘿,你想要成為未來的Acmer嗎?那就必須幫Lrc算出他的分數咯O(∩_∩)O~
----------------------------------------------------------------------------------------------------------------------------------------
   PS:記住,Acmer要有永不言棄的精神,花一天過了這個題,值得了~~~~~

(出題人Ly)

 

 

輸入格式

    第一行3個整數n(n<=150),m(m<=20),k(0=<k<=1e9+7),分別代表這一局出現的水果(炸彈)數,水果種類,以及每切中一個炸彈要扣的分數。
緊接着一行m個整數v1,v2,v3….vm(0<=vi<=10000),分別代表m種水果的分數。
接下來n行分別代表一個水果(炸彈),每行4個整數x,y,p,t(x,y代表該水果的坐標,p代表該水果得種類(p=0時為炸彈),t代表該水果出現的時刻)(-1000<=x,y<=1000,0<=p<=m,1<=t<=90)。

 

輸出格式

若Lrc 得到的分數小於或者等於零,請輸出“Poor Lrc!!!”,因為他將要受到強神的BS;
否則,輸出“God Lrc, you’ve got S points!”,S是Lrc得到的分數。

 

輸入樣例

sample#1:
1 1 10
10
0 0 1 1

sample#2:
6 3 20
1 2 10
0 1 2 1
100 100 2 2
0 2 0 1
0 3 3 1
1 1 3 2
0 0 1 1

 

輸出樣例

sample#1:
God Lrc, you’ve got 10 points!

sample#2:
God Lrc, you’ve got 22 points!

 

Hint

樣例中的sample#1,sample#2不屬於輸入輸出的一部分,只是說明其不是一個輸入和輸出。

Attention: 炸彈當一個水果就行了!!切到炸彈不結束!!

 

 

顯然是幾何,關於直線和點的。這題還是水題,只要不想復雜了就好,其水題的本質是因為數據規模太小,直接暴力枚舉,而且期間不需要long long變量,全部用int

1.對所有的水果從0到n-1標號,按時間從小到大排序,每次只能處理同一時間出現的水果,也就是按區間處理,接下來是怎么處理一個區間(相同時間)的水果

2.注意一點,每個時間只能切一刀,一刀只能切一條直線上的水果,然后不一定要切完整條直線上,可以只切這個直線上的一段子序列,甚至可以不切(當切的最優解都小於0的時候,那倒不如不切,這樣值為0,才是最優解,我就是差了這個判斷WA了一次,其實可以將最優值初始化為0,那么就不用做這個判斷了)

說到這里就想到了什么呢?先找到所有可能的直線,把一條直線上的點全部找出來,保存下來還要按直線位置排好,然后最大子序列求和(因為可以切直線上的一部分,只能一次,顯然就是最大子序列和),但是細想這種算法顯然是不行的,太麻煩,實現起來很難受,再看看數據規模不大,其實只要枚舉所有的起點和終點(我的代碼中起點和終點是不同的),那么就得到一條線段,再枚舉所有其他的點在不在線段內(不在延長線),然手算出這條線段的和,這樣子其實無形中解決了子序列和的問題,因為枚舉起點和終點其實不止枚舉了所有直線還枚舉所有的子序列

計算完線段的和並找到了最大值,別忘了這是線段,沒有包含只切一個點的情況,所有逐一判斷只切一個點的時候權會不會更大,然后更新

最后,就得到了切下去的時候的最優解,看看這個最優解是否大於0,是的話那它當然是最優解,不是的話,顯然不應該切下去,直接拋棄答案,認為這些切割的得分為0

 

因為整個代碼其實都是枚舉的多,要說用了知識點就兩個

1.計算任意兩個點(同一時間的任意兩個點)的斜率,斜率用分數保存,這樣可以避免精度問題,斜率不存在的用INF表示

2.判斷一個點c在不在線段ab上(c不是a,b兩個端點)

判別方法:ac的斜率和ab的斜率相同,兩個線段斜率相等且過一個公共點a,那么3點共線。單單3點共線還不行,要判斷不在延長線和反向延長線上,那就是判斷點c在線段ab作為對角線的矩形內部,具體看代碼中的on_segment(i,j,k)函數

 

代碼寫的匆忙,也沒有整理,湊合着看吧,本身題目不難

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 160
#define M 30
#define INF 0x3f3f3f3f
struct fff  //水果信息
{
    int x,y,v,t;
}a[N],b[N]; //v是水果的價值,一開始是種類,再變為價值
struct kkk  //建圖保存任意兩點的斜率,用分數保存
{
    int y,x;
}g[N][N];
int n,mm,K;  //水果個數,種類數,炸彈價值
int m[M];

int min(int x ,int y)
{ return x<y?x:y; }
int max(int x , int y)
{ return x>y?x:y; }


int cmp(struct fff p , struct fff q)
{ return p.t<q.t; }
/*
void printfff()
{
    printf("______________\n");
    for(int i=0; i<n; i++)
        printf("%d %d %d %d\n",a[i].x,a[i].y,a[i].v,a[i].t);
    printf("______________\n");
    return ;
}
*/
int on_segment(int i , int j ,int k)
{//判斷點k在不在線段(i,j)上
    //先判斷斜率是否相同
    int y1,y2,x1,x2;
    y1=g[i][j].y;  x1=g[i][j].x;
    y2=g[i][k].y;  x2=g[i][k].x;
    if(y1*x2!=y2*x1)  return 0;  //斜率不同

    if( min(a[i].x,a[j].x)<=a[k].x && a[k].x<=max(a[i].x,a[j].x) && 
        min(a[i].y,a[j].y)<=a[k].y && a[k].y<=max(a[i].y,a[j].y) )
        return 1;
    else 
        return 0;
}
int max_sum(int p , int q)  //求[p,q]區間內的最大值,這一區間的水果是同一時間
{
    int maxsum,sum;
    int i,j;
    /*=======保存任意兩點的斜率========*/
    memset(g,0,sizeof(g));
    for(i=p; i<=q; i++)
        for(j=p; j<=q; j++)
            if(i!=j)  //求任意兩個水果i,j的斜率
            {
                int yy,xx;
                yy=a[i].y-a[j].y;
                xx=a[i].x-a[j].x;
                if(xx==0)  //斜率不存在,設為無窮大
                { g[i][j].y=INF; g[i][j].x=1; } 
                else  //斜率存在,用分數保存
                { g[i][j].y=yy; g[i][j].x=xx; }
            }
/*
    printf("該區間內任意兩點(不同)的斜率\n");
    for(int i=p; i<=q; i++)
        for(int j=p; j<=q; j++)
            if(i!=j)
                printf("(%d,%d)  %d\\%d\n",i,j,g[i][j].y,g[i][j].x);
*/
    /*=======保存任意兩點的斜率========*/

    //枚舉所有的起點和終點,然后判斷那些點在這個線段上(注意是線段)
    //然后把線段上的點的和全部求出來,這樣子其實已經解決了子序列求和的問題
    //關鍵是怎么判斷一點在一個線段上
    maxsum=-INF;
    for(int i=p; i<=q; i++)
        for(int j=p; j<=q; j++)
            if(i!=j)  //枚舉線段的所有起點和終點
            {
                sum=a[i].v+a[j].v;
                //printf("枚舉的起點和終點為:  %d   %d  其實和為%d+%d=%d\n",i,j,a[i].v,a[j].v,sum);
                for(int k=p; k<=q; k++)  //枚舉所有可能的點k,看是否在線段上
                    if(k!=i && k!=j)
                    {
                        if( on_segment(i,j,k) ) //點k在線段(i,j)上
                        {
                            //printf("%d在線段上\n",k);
                            sum+=a[k].v;
                        }
                    }
                //printf("最終該線段的和:%d\n",sum);
                if(sum>maxsum) maxsum=sum;
            }
    //printf("======只切單獨的一個點======\n");
    for(int i=p; i<=q; i++)
        if(a[i].v>maxsum) maxsum=a[i].v;
    if(maxsum<0)  return 0;  //如果切了分數反而小於0,那么就不要切
    return maxsum;
}
void solve()
{
    int sum;
    int i,j;
    a[n].t=0;  //保險處理
    sum=0;
    for(i=0; i<n; )  //掃描所有的水果
    {
        int t=a[i].t;
        for(j=i; j<n; j++)
            if(a[j+1].t!=t) break;
        //得到區間[i,j],閉區間,是相同時間的,那么就求這段時間里面的最大值
        //printf("打印區間[%d , %d]\n",i,j);
        int tmp=max_sum(i,j);
        //printf("該區間的最大值%d\n",tmp);
        sum+=tmp;
        i=j+1; //從下一個點開始
    }
    //printf("最后的得分%d\n",sum);
    if(sum<=0)  printf("Poor Lrc!!!\n");
    else        printf("God Lrc, you’ve got %d points!\n",sum);
    return ;
}
int main()
{
    while(scanf("%d%d%d",&n,&mm,&K)!=EOF)
    {
        for(int i=1; i<=mm; i++)  //每種水果的價值
            scanf("%d",&m[i]);
        m[0]=-K;  //炸彈價值

        for(int i=0; i<n; i++)  //n個水果的信息
        {
            scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].v,&a[i].t);
            a[i].v=m[a[i].v];
        }
        sort(a,a+n,cmp);
        //printfff();
        solve();
    }
    return 0;
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM