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;
}

对于第五题来说,是一个卡特兰数,金桔告诉我的,我并没有看出来,卡特兰数我练的也不熟,想了一个多小时没有思路就放弃了,赛后看了大牛们的代码,卡特兰数+扩展欧几里得,我还是慢慢研究这个题吧。


以上全部题目来自:http://bestcoder.hdu.edu.cn/contests/contest_show.php?cid=774





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值