1. 程式人生 > >2017Summmer_上海金馬五校 F題,G題,I題,K題

2017Summmer_上海金馬五校 F題,G題,I題,K題

sum 組隊 時序 而且 sizeof bit 屬性排序 print 浪費

以下題目均自己搜

F題 A序列

一開始真的沒懂題目什麽意思,還以為是要連續的子串,結果發現時序列,簡直智障,知道題意之後,好久沒搞LIS,有點忘了,復習一波以後,直接雙向LIS,處理處兩個數組L和R,然後對整個數組掃一遍對於每一個下標取m=min(L[i],R[i]);用ans取2*m-1中的最大值。LIS用nlogn的算法實現,二分用的是lower_bound(),直接看代碼。

//Author: xiaowuga
#include <bits/stdc++.h>
#define maxx INT_MAX
#define minn INT_MIN
#define inf 0x3f3f3f3f
const
long long N=500003; using namespace std; typedef long long LL; int dp[N]; int L[N],R[N]; int main() { ios::sync_with_stdio(false);cin.tie(0); int n; int a[N]; while(cin>>n){ for(int i=0;i<n;i++){ cin>>a[i]; } memset(dp,inf,sizeof(dp));
for(int i=0;i<n;i++){ int pos=lower_bound(dp,dp+n,a[i])-dp; dp[pos]=a[i]; L[i]=pos+1; } memset(dp,inf,sizeof(dp)); for(int i=n-1;i>=0;i--){ int pos=lower_bound(dp,dp+n,a[i])-dp; dp[pos]=a[i]; R[i]
=pos+1; } for(int i=0;i<n;i++) cout<<L[i]<<" "; cout<<endl; for(int i=0;i<n;i++) cout<<R[i]<<" "; cout<<endl; int ans=minn; int tmp; for(int i=0;i<n;i++){ int tmp=min(L[i],R[i]); ans=max(tmp*2-1,ans); } cout<<ans<<endl; } return 0; }

G題 戰鬥

暴力階乘題,因為n<10,所以暴力枚舉全排列所有的出戰順序,然後模擬和電腦去打就好了,復雜度n*n!。不要真的老實的的每一次攻擊的去模擬,萬一兩個兩個怪獸都是1000的血1的攻擊力,一次枚舉,十個怪獸就是10000的計算計算量,而你的枚舉量可能高達10!三百多萬啊,絕逼超時GG,所以直接除找到每組隊長的怪獸需要各自攻擊對方幾次才會死,然後取最小值,將hp減去攻擊次數*攻擊力,得到怪獸戰後的狀態,對於hp<=0的我們就換人。還有不要確切的計算具體要攻擊多少次,因為有的時候比如你是9的hp,5的五的攻擊力,9/5=1,實際上要攻擊兩次才會死攻擊次數是hp/at+1。然後如果雙方的hp變成10,10/5=2,剛好死亡攻擊次數恰好是hp/at,這個時候如果用%取判斷了話,是會超時的,因為%與運算是比較慢的。所以我們用一個while循環,模擬最後一次攻擊,將會比算出這個具體的攻擊次數速度要快。這就是我為什麽一開始超時的原因,還有一個優化就是要枚舉的時候都要復制一遍雙方的怪獸信息,因為只有當前的怪獸有用,所以我們用一個值來存就好了,如果你用結構體,每次就有一個攻擊實際上不用復制但是復制了,實際上浪費了時間,面對10!的枚舉量,也會慢很多,然後就是邊打邊復制,這樣有的時候電腦其實一只怪獸就團滅你了,但是你卻多復制了其他怪獸同樣浪費時間。

最後還有一個逆天優化:就是我們每次記住上一次枚舉出場的順序,如果電腦兩只怪獸就把你團滅了,那麽如果你下一次枚舉前兩只怪獸出場順序沒有發生改變,那麽電腦還是可以用兩只怪獸把你團滅,無論你後面的怪獸如何出出站,壓倒性的實力。這樣的枚舉是無用的,如果對他的戰鬥進行模擬,又會有大量的復制會是無用功,而且對於字典序全排列,大多數的枚舉的前幾位都和上一次枚舉基本是一樣的。這就給我們啟發,使我們可以快速的跳過一下無用的枚舉,將n*n!的復雜度,優化成n!,加快了近十倍的速度。大概只需要100ms就可以ac,當然後還有2^n復雜度的做法,那個就更快了,需要用到狀態壓縮dp,本人目前不會。

//Author: xiaowuga
#include <bits/stdc++.h>
#define maxx INT_MAX
#define minn INT_MIN
#define inf 0x3f3f3f3f
const long long N=11; 
using namespace std;
typedef long long L;
struct M{
    int at,hp,pos;
    bool operator <(const M &m) const{
        return at<m.at;
    }
}me[N],co[N];
M cm[N],mm[N];
int main() {
    int T,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=0;i<n;i++) scanf("%d%d",&co[i].hp,&co[i].at);
        for(int i=0;i<n;i++) {scanf("%d%d",&me[i].hp,&me[i].at);}
        int flag=0,flag2=0;
        int c1=0,c2=0,m;
        sort(me,me+n);
        do{
            if(flag2){
                if(c1==0) break;
                else{
                    int ifsame=1;
                    for(int i=0;i<=c1;i++){
                        if(me[i].at!=mm[i].at) {ifsame=0;break;}
                    }
                    if(ifsame) continue;
                 }
            }
            flag2=1;
            c1=0;c2=0;
            cm[0]=co[0]; mm[0]=me[0];
            while(c1<n&&c2<n){
                m=min(cm[c1].hp/mm[c2].at,mm[c2].hp/cm[c1].at);
                cm[c1].hp-=m*mm[c2].at;
                mm[c2].hp-=m*cm[c1].at;
                while(cm[c1].hp>0&&mm[c2].hp>0){
                    cm[c1].hp-=mm[c2].at;
                    mm[c2].hp-=cm[c1].at;
                }
                if(cm[c1].hp<=0) {c1++;cm[c1]=co[c1];}
                if(mm[c2].hp<=0) {c2++;mm[c2]=me[c2];}
            }
            if(c1==n&&c2<n) {flag=1;break;}
        }while(next_permutation(me,me+n));
        if(flag) printf("YES\n");
        else printf("NO\n");    
    }
    return 0;
}

I題 丟史蒂芬妮

博弈xjb搜題,博弈論知識不好,別人告訴我怎麽搜的。xjb搜了一波,就過了,不是特別理解。直接代碼吧!

//Author: xiaowuga
#include<cstdio>
#define maxx INT_MAX
#define minn INT_MIN
#define inf 0x3f3f3f3f
const long long N=505; 
using namespace std;
typedef long long L;
int vis1[N]={0};
int num=0;
int primnum_list[N];
void make_primnum(){
    for(int i=2;i<N/2;i++)
        for(int j=2*i;j<N;j+=i){
            vis1[j]=1;
        }
    for(int i=2;i<N;i++){
        if(vis1[i]==0) primnum_list[num++]=i;
    } 
}
int mat[N][N]={0},vis[N][N]={0};
int dfs(int x,int y){
    if(vis[x][y]) return mat[x][y];
    vis[x][y]=1;
    for(int i=0;i<num;i++){
        int t=primnum_list[i];
        if(x-t>0) mat[x][y] |=!(dfs(x-t,y));
        if(y>t>0) mat[x][y] |=!(dfs(x,y-t));
        if(x-t>0&&y-t>0) mat[x][y] |=!(dfs(x-t,y-t));
    }
    return mat[x][y];
}
int main() {
    make_primnum();
    for(int i=1;i<=500;i++)
       for(int j=1;j<=500;j++){
            mat[i][j]=dfs(i,j);
       }
    int T;
    scanf("%d",&T);
   while(T--){
        int n,m;
        scanf("%d%d",&n,&m);
        if(mat[n][m]) printf("Sora\n");
        else printf("Shiro\n");
   } 
    return 0;
}

K題 購買裝備

第一眼尼瑪不是一個背包dp嗎?還是0-1背包,結果native了,10000的物品,一億的預算,背包的復雜度更本跑不出來,所以我們采用貪心的思路。

我們首先要把問題分開去思考,這樣有助於我們思考,而不會因為問題整體比較比較復雜,從而在思考上停滯不前,應該要把問題什麽事主要問題什麽是次要問題,題目中次要問題,實在主要問題的限制之下,也就是在購買盡量多的物品的情況下,物品中屬性最小的值盡量大。首先是購買盡量多的物品,由於每個物品只能買一次,所以買便宜的物品,剩下的錢更多,也就可以買更多的物品,所以貪心對價格排序得到最大可以購買的數量m,但是這不一定是最小屬性最大的。所以我們接著看後半部分的問題,問題就變成了在n個物品裏面選m個其中屬性最小的的物品的值要盡量大,標準的最小值最大化的套路。對屬性排序,二分枚舉屬性,在大於等於當前枚舉的最小屬性中,對價格排序取 前m個,判斷是否小於等於預算,這個是學長的做法,我也想過。但是每次復制一遍再排序,所以覺得沒可行,所以沒敢試。誰知學長用了特殊姿勢。用了nth_element()這個庫函數,使用m次,直接排序復雜度nlogn,減去復制的過程防止復雜度上升到n^2logn,從而使復雜度變為nlognlogn,這個復雜度可以滿足題目的數據量。但是我想到是另一個種方法,前面和學長一樣得到m的最大購買數量,然後對屬性排序(從大到小),取前m個元素判斷是否滿足預算(枚舉下標為m-1的元素為最小屬性),不滿足則枚舉下標為m的元素為最小屬性,從前m+1個元素裏取m個最便宜的看一下,是否滿足預算,不滿足再次往後枚舉,直到滿足要求,我也需要復制一遍再排序,然而我想到用優先隊列優化,建立一個大小為m的優先隊列,計算一個堆的價格總和,然後每次枚舉我們把堆頂元素pop,將枚舉的元素插入,計算總價格的改變值,判斷是否滿足要求,直到滿足要求位置。由於刪除和插入的操作復雜度都是logn,所以總體的復雜度是nlogn,以下是我的代碼

//Author: xiaowuga
#include <bits/stdc++.h>
#define maxx INT_MAX
#define minn INT_MIN
#define inf 0x3f3f3f3f
const long long N= 1e5+10;
using namespace std;
typedef long long L;
priority_queue<long long, vector<long long>,less<long long> >q;
struct equip{
    long long a,b;
}oj[N];
bool cmp1(equip x,equip y){
    return x.b<y.b;
}
bool cmp2(equip x,equip y){
    return x.a>y.a;
}
int main() {
    long long T,n,m;
    scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld",&n,&m);
        for(int i=0;i<n;i++) scanf("%lld%lld",&oj[i].a,&oj[i].b);
        sort(oj,oj+n,cmp1);
        long long sum=0,k=0;
        for(int i=0;i<n;i++){
            if(sum+oj[i].b<=m){sum+=oj[i].b;k++;}
            else break;
        }
        sort(oj,oj+n,cmp2);
        sum=0;
        while(!q.empty()) q.pop();
        for(int i=0;i<k;i++){
            q.push(oj[i].b);
            sum+=oj[i].b;
        }
        if(sum<=m) {printf("%lld %lld\n",k,oj[k-1].a);continue;}
        for(int i=k;i<n;i++){
            sum=sum-q.top()+oj[i].b;
            if(sum<=m){ printf("%lld %lld\n",k,oj[i].a); break; }
            q.pop();q.push(oj[i].b);
        }
    } 
    return 0;
}

繼續補題,這個隨筆將會在最近持續更新,盡量把金馬五校的題補完吧!!!

2017Summmer_上海金馬五校 F題,G題,I題,K題