1. 程式人生 > >2017百度之星資格賽總結

2017百度之星資格賽總結

第一題沒有什麼說的,題目我都沒看懂,並沒有理解他的包圍,賽後大佬的程式碼我也看了,嗯,沒看懂。。。

第二題是資料水了,正解為全域性最小割,我並沒這樣做出來,我只是單純的用並查集刪了點(可用單鏈卡掉),竟然過了。。(據說資料水到不用並查集也可以過)

度度熊的王國戰略

Accepts: 674 Submissions: 6056 Time Limit: 40000/20000 MS (Java/Others) Memory Limit: 32768/132768 K (Java/Others) Problem Description

度度熊國王率領著喵哈哈族的勇士,準備進攻嘩啦啦族。

嘩啦啦族是一個強悍的民族,裡面有充滿智慧的謀士,擁有無窮力量的戰士。

所以這一場戰爭,將會十分艱難。

為了更好的進攻嘩啦啦族,度度熊決定首先應該從內部瓦解嘩啦啦族。

第一步就是應該使得嘩啦啦族內部不能同心齊力,需要內部有間隙。

嘩啦啦族一共有n個將領,他們一共有m個強關係,摧毀每一個強關係都需要一定的代價。

現在度度熊命令你需要摧毀一些強關係,使得內部的將領,不能通過這些強關係,連成一個完整的連通塊,以保證戰爭的順利進行。

請問最少應該付出多少的代價。

Input

本題包含若干組測試資料。

第一行兩個整數n,m,表示有n個將領,m個關係。

接下來m行,每行三個整數u,v,w。表示u將領和v將領之間存在一個強關係,摧毀這個強關係需要代價w

資料範圍:

2<=n<=3000

1<=m<=100000

1<=u,v<=n

1<=w<=1000

Output

對於每組測試資料,輸出最小需要的代價。

Sample Input
2 1
1 2 1
3 3
1 2 5
1 2 4
2 3 3
Sample Output
1
3

程式碼:

#include <cstdio>
#include <cstring>
#include <iostream>
#define inf 0x3f3f3f3f
using namespace std;
long long num[100010];
int n,m;
int pre[3001];
void init(){
    for(int i=1;i<3001;i++)
        pre[i]=i;
}
int Find(int x){
    if(x!=pre[x])
        return pre[x]=Find(pre[x]);
    return x;
}
void mix(int x,int y){
    int xx=Find(x),yy=Find(y);
    if(xx!=yy){
        pre[xx]=yy;
    }
}
int main(){
    int a,b,c;
    while(~scanf("%d %d",&n,&m)){
        bool flag=false;
        memset(num,0,sizeof(num));
        init();
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&a,&b,&c);
            if(a!=b){
                num[a]+=c;
                num[b]+=c;
                mix(a,b);
            }
        }
        int cnt=0;
        for(int i=1;i<=n;i++){
            if (pre[i]==i)
                cnt++;
        }
        if(cnt==1)
            flag=true;
        if(flag){
            long long ans=inf;
            for(int i=1;i<=n;i++){
                if(ans>num[i])
                    ans=num[i];
            }
            printf("%I64d\n",ans);
        }
        else {
            printf("0\n");
        }
    }
    return 0;
}

正解:
#include<bits/stdc++.h>
#define N 3005
#define mk make_pair
using namespace std;
int a[N][N],T[N][N],F[N],vis[N];
int dead[N],pos[N],u,v,w,i,j,n,m,ans;
struct Heap{
  int id[N],len;
  void up(int x){
    for (;x>1;x>>=1)
      if (F[id[x]]>F[id[x>>1]])
        swap(pos[id[x]],pos[id[x>>1]]),swap(id[x],id[x>>1]);
      else return;
  }
  void pop(){
    pos[id[len]]=0;if ((--len)==0) return;
    id[1]=id[len+1];pos[id[1]]=1;
    for (int x=1;(x<<1)<=len;){
      int y=((x<<1)==n||F[id[x<<1]]>F[id[x<<1|1]])?(x<<1):(x<<1|1);
      if (F[id[x]]>=F[id[y]]) return;
      swap(pos[id[x]],pos[id[y]]);swap(id[x],id[y]);x=y;
    }
  }
  void add(int x){
    if (!pos[x]) id[pos[x]=++len]=x,up(len);
          else   up(pos[x]);
  }
  void clear(){len=0;}
}G;
void work(){
  for (int i=1;i<=n;i++)
    F[i]=-1e9,vis[i]=pos[i]=0;
  int S,p,q;
  for (int i=1;i<=n;i++) 
    if (!dead[i]) {S=i;break;}
  G.clear();F[S]=0;
  G.len=1;pos[G.id[1]=S]=G.id[1];
  while (G.len){
    int x=G.id[1];vis[x]=1;G.pop();
    for (i=1;i<=*a[x];i++){
      int y=a[x][i];
      if (vis[y]) continue;
      F[y]+=T[x][y];G.add(y);
    }
    p=q;q=x;
  }int sum=0;
  for (int i=1;i<=*a[q];i++)
    sum+=T[q][a[q][i]];
  //printf("%d\n",sum);
  ans=min(ans,sum);
  for (int i=1;i<=n;i++)
    if (!dead[i]&&i!=q&&i!=p)
      if (T[q][i]&&!T[p][i]) a[p][++*a[p]]=i;
  for (int i=1;i<=n;i++)
    if (!dead[i]&&i!=p&&i!=q)
      T[i][p]+=T[i][q],T[p][i]+=T[q][i];
  dead[q]=1;
  //printf("%d %d\n",p,q);
  int debug=(p==4&&q==3);
  for (int i=1;i<=n;i++)
    if (!dead[i]&&i!=q){
      int j=1;
      for (;j<=*a[i];j++)
        if (a[i][j]==q) break;
      if (j<=*a[i]){
        for (;j<*a[i];j++)
          a[i][j]=a[i][j+1];
        --(*a[i]);
      }
      if (!T[i][p]) continue;
      for (j=1;j<=*a[i];j++)
        if (a[i][j]==p) break;
      if (j>(*a[i])) a[i][++*a[i]]=p;
    }
  
  //printf("Delete:%d Sum:%d\n",q,sum);
  //for (int i=1;i<=n;i++)
    //if (!dead[i]) for (int j=1;j<=*a[i];j++)
      //printf("%d->%d %d\n",i,a[i][j],T[i][a[i][j]]);
  
}
int main(){
  while (scanf("%d%d",&n,&m)!=EOF){
    if (!n) return 0;
    memset(T,0,sizeof(T));
    for (i=1;i<=m;i++){
      scanf("%d%d%d",&u,&v,&w);
      if (u==v) continue;
      T[u][v]+=w;T[v][u]+=w;
    }
    //printf("%d\n",T[1][1]);
    for (i=1;i<=n;i++)
      for (j=1;j<=n;j++)
        if (T[i][j]) a[i][++*a[i]]=j;
      
    /*for (int i=1;i<=n;i++)
      for (int j=1;j<=*a[i];j++)
        printf("%d->%d %d\n",i,a[i][j],T[i][a[i][j]]);*/
    ans=2e9;
    for (int tmp=min(n-1,1000);tmp;tmp--)
      work();
    printf("%d\n",ans);
    for (i=1;i<=n;i++) *a[i]=dead[i]=0;
  }
}

第三個題是簽到題,我們先判斷是否能夠打敗所有怪獸,然後就是一個裸揹包。

度度熊與邪惡大魔王

Accepts: 3666 Submissions: 22474 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Description

度度熊為了拯救可愛的公主,於是與邪惡大魔王戰鬥起來。

邪惡大魔王的麾下有n個怪獸,每個怪獸有a[i]的生命值,以及b[i]的防禦力。

度度熊一共擁有m種攻擊方式,第i種攻擊方式,需要消耗k[i]的晶石,造成p[i]點傷害。

當然,如果度度熊使用第i個技能打在第j個怪獸上面的話,會使得第j個怪獸的生命值減少p[i]-b[j],當然如果傷害小於防禦,那麼攻擊就不會奏效。

如果怪獸的生命值降為0或以下,那麼怪獸就會被消滅。

當然每個技能都可以使用無限次。

請問度度熊最少攜帶多少晶石,就可以消滅所有的怪獸。

Input

本題包含若干組測試資料。

第一行兩個整數n,m,表示有n個怪獸,m種技能。

接下來n行,每行兩個整數,a[i],b[i],分別表示怪獸的生命值和防禦力。

再接下來m行,每行兩個整數k[i]和p[i],分別表示技能的消耗晶石數目和技能的傷害值。

資料範圍:

1<=n<=100000

1<=m<=1000

1<=a[i]<=1000

0<=b[i]<=10

0<=k[i]<=100000

0<=p[i]<=1000

Output

對於每組測試資料,輸出最小的晶石消耗數量,如果不能擊敗所有的怪獸,輸出-1

Sample Input
1 2
3 5
7 10
6 8
1 2
3 5
10 7
8 6
Sample Output
6
18
程式碼:
#include <cstdio>
#include <cstring>
#include <iostream>
#define inf 0x3f3f3f3f
#define LL long long
using namespace std;
int n,m,a[100010],b[100010];
int k[10010],p[10010];
long long dp[12][1005<<1];
int main(){
    while(~scanf("%d%d",&n,&m)){
        int mx0=0,mx1=0;
        for(int i=1;i<=n;i++){
            scanf("%d%d",&a[i],&b[i]);
            mx0=max(mx0,b[i]);
        }

        for(int i=1;i<=m;i++){
            scanf("%d%d",&k[i],&p[i]);
            mx1=max(mx1,p[i]);
        }
        if(mx0>=mx1){
            printf("-1\n");
            continue;
        }
        memset(dp,inf,sizeof(dp));
        for(int x=0;x<=10;x++){
            dp[x][0]=0;
            for(int i=1;i<=m;i++){
                if(p[i]<=x)
                    continue;
                for(int j=p[i]-x;j<=2000;j++){
                    dp[x][j]=min(dp[x][j],dp[x][j-(p[i]-x)]+k[i]);
                }
            }
            for(int i=2000;i>0;i--){
                dp[x][i]=min(dp[x][i],dp[x][i+1]);
            }
        }
        LL ans=0;
        for(int i=1;i<=n;i++){
            ans+=dp[b[i]][a[i]];
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

第四個題是一個比較有難度的dp,這個題多虧了qls和csy在ACfun裡的提示,題意沒有很清楚,這其實就是一個01揹包,但是要求優先最大化總得分,然後最小化序列之和,最後最大化花費,要求輸出字典序最小的解,在不打飯的情況下不輸出空行(我PE了4發...)。

度度熊的午飯時光

Accepts: 823 Submissions: 9241 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Description

度度熊最期待每天的午飯時光,因為早飯菜品清淡,晚飯減肥不敢吃太多(胖紙的憂傷T.T)。

百度食堂的午餐超級豐富,祖國各大菜系應有盡有,度度熊在每個視窗都有愛吃的菜品,而且他還為喜愛的菜品打了分,吃貨的情懷呀(>.<)。

但是,好吃的飯菜總是很貴,每天的午飯預算有限,請幫度度熊算一算,怎樣打飯才能買到的最好吃的飯菜?(不超過預算、不重樣、午餐等分最高的情況下,選擇菜品序號加和最小,加和相等時字典序最小的組合)

Input

第一行一個整數T,表示T組資料。 每組測試資料將以如下格式從標準輸入讀入:

B

N

score_1 cost_1

score_2 cost_2

:

score_N cost_N

第一行,正整數B(0 <= B <= 1000),代表午餐的預算。

第二行,正整數N (0 <= N <= 100),代表午餐可選的菜品數量

從第三行到第 (N + 2) 行,每行兩個正整數,以空格分隔,score_i表示菜品的得分,cost_i表示菜品的價格(0 <= score_i, cost_i <= 100)。

Output

對於每組資料,輸出兩行: 第一行輸出:"Case #i:"。i代表第i組測試資料。 第二行輸出菜品的總得分和總花費,以空格分隔。 第三行輸出所選菜品的序號,菜品序號從1開始,以空格分隔。

Sample Input
2
29
6
9 10
3 4
6 5
7 20
10 9
15 11
0
2
2 23
10 12
Sample Output
Case #1:
34 29
2 3 5 6
Case #2:
0 0

這個題開始用揹包的思路去想的

pre[i][j].first代表的是你取的最後一個物品編號,因為dp就是物品編號小到大的順序dp的,所以這個一定是當前最小

dp[i][j].second代表的是當前序列之和,如果最大值相同,取序號和最小

dp[i][j].first代表的是遍歷到第i個物品總費用為j的情況下的最大價值

然後看程式碼應該就懂了

程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
struct node{
    int score,cost;
}xin[10001];
pair<int,int>dp[102][1001],pre[102][1001];
int p[10000];
int main(){
    int Case=0,t;
    int b,n;
    scanf("%d",&t);
    while(t--){
        Case++;
        scanf("%d%d",&b,&n);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&xin[i].score,&xin[i].cost);
        }
        for(int i=0;i<=n;i++){
            for(int j=0;j<=b;j++){
                dp[i][j].first=dp[i][j].second=-1;
                pre[i][j].first=pre[i][j].second=-1;
            }
        }
        dp[0][0].first=dp[0][0].second=0;
        int ans1=0,ans2=0,x,y;
        for(int i=1;i<=n;i++){
            for(int j=xin[i].cost;j<=b;j++){
                for(int k=0;k<i;k++){
                    if(dp[k][j-xin[i].cost].first==-1)
                        continue;
                    if(dp[i][j].first<dp[k][j-xin[i].cost].first+xin[i].score){
                        dp[i][j].first=dp[k][j-xin[i].cost].first+xin[i].score;
                        dp[i][j].second=dp[k][j-xin[i].cost].second+i;
                        pre[i][j].first=k;
                        pre[i][j].second=j-xin[i].cost;
                    }
                    else if(dp[i][j].first==dp[k][j-xin[i].cost].first+xin[i].score){
                        if(dp[i][j].second>dp[k][j-xin[i].cost].second+i){
                            dp[i][j].second=dp[k][j-xin[i].cost].second+i;
                            pre[i][j].first=k;
                            pre[i][j].second=j-xin[i].cost;
                        }
                    }
                }
                if(dp[i][j].first>ans1){
                    ans1=dp[i][j].first;
                    ans2=dp[i][j].second;
                    x=i,y=j;
                }
                else if(dp[i][j].first==ans1){
                    if(ans2>dp[i][j].second){
                        ans2=dp[i][j].second;
                        x=i,y=j;
                    }
                }
            }
        }
        printf("Case #%d:\n",Case);
        printf("%d %d\n",ans1,y);
        int cnt=0;
        while(x!=0&&x!=-1){
            p[cnt++]=x;
            int k1=x,k2=y;
            x=pre[k1][k2].first;
            y=pre[k1][k2].second;
        }
        for(int i=cnt-1;i>=0;i--){
            printf(i!=0?"%d ":"%d\n",p[i]);
        }
    }
    return 0;
}

對於第五題來說,是一個卡特蘭數,金桔告訴我的,我並沒有看出來,卡特蘭數我練的也不熟,想了一個多小時沒有思路就放棄了,賽後看了大牛們的程式碼,卡特蘭數+擴充套件歐幾里得,我還是慢慢研究這個題吧。