BUAA-SCSE 暑期算法提高班 Final Contest 解题报告

http://www.bnuoj.com/bnuoj/contest_show.php?cid=1847


A

给定两个字符串,两个字符串的字符之间存在一种双射关系,求是否存在这样一组映射?

只需统计两个字符串的各个字符的出现次数,然后排序,从下到大看出现次数是否相等即可。

  scanf("%s\n%s",s1,s2);
  for (i = 0 ;i < strlen(s1) ;i ++)  
    a[s1[i] - 'A'] ++;
  for (i = 0 ;i < strlen(s2) ;i ++)  
    b[s2[i] - 'A'] ++;
  sort(a , a + 26);
  sort(b , b + 26);
  for (i = 0 ;i < 26 ; i ++)
    if (a[i] != b[i])
      break;
  if (i == 26)
    printf("YES");
  else printf("NO");
  return 0; 

B

一个容器,可以插入元素也可以弹出元素,告诉你插入弹出的是什么,问你这个容器是栈,队列还是优先队列

直接用STL模拟吧,因为操作数非常少,用数组模拟也是可行的。

  int i , x , y;
  stack<int> s;
  queue<int> q;
  priority_queue<int> p;
  bool fs = 0, fq = 0, fp = 0;
  while (n --)
  {
    cin >> i >> x;
    if (i == 1)
    {
      if(!fs) s.push(x);
      if(!fq) q.push(x);
      if(!fp) p.push(x);
    }
    else
    {
      if (s.empty())
        fs = 1;
      else if (!fs)
      {
        y = s.top() , s.pop();
        if (y != x) fs = 1;
      }
      if (q.empty())
        fq = 1;
      else if (!fq)
      {
        y = q.front() , q.pop();
        if (y != x) fq = 1;
      }
      if (p.empty())
        fp = 1;
      else if (!fp)
      {
        y = p.top() , p.pop();
        if (y != x) fp = 1;
      }
    }
  }
  if (!fs && fp && fq) puts("stack");
  else if (fs && fp && !fq) puts("queue");
  else if (fs && !fp && fq) puts("priority queue");
  else if (fs && fp && fq) puts("impossible");
  else puts("not sure");

C

简单数位DP,输出第n个包含666的数字,二分即可……

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <climits>
using namespace std;
#define N 100005
#define M 500005
int X , Y , ca;
long long dp[20][4];
int digit[20];

long long dfs(int pos , int have , bool doing)
{
    if(pos == -1)
        return have == 3;

    if(!doing && ~dp[pos][have])
        return dp[pos][have];

    long long ans = 0;
    int end = doing ? digit[pos] : 9;
    for(int i = 0 ; i <= end ; i ++)
    {
        int nhave = have;
        if (have == 0)
        {
          if (i == 6)
            nhave = 1;
          else nhave = 0;
        }
        else if (have == 1)
        {
          if (i == 6)
            nhave = 2;
          else nhave = 0;
        }
        else if (have == 2)
        {
          if (i == 6)
            nhave = 3;
          else nhave = 0;
        }
        ans += dfs(pos - 1 , nhave , doing && i == end);
    }
    if(!doing)
      dp[pos][have] = ans;
    return ans;
}

long long cal(long long x)
{
    memset(dp , -1 , sizeof(dp));
    int pos = 0;
    while(x)
    {
      digit[pos ++] = x % 10;
      x /= 10;
    }
    return dfs(pos - 1 , 0 , 1);
}

int ai(int a , int b)
{
  char s[20];int i , j , ans = 0 , x , y;
  for (i = a ; i <= b ; ++ i)
  {
    sprintf(s ,"%d" , i);
    if (strstr(s , "666")) ++ ans;
  }
  return ans;
}

void work()
{
  long long m , l , r , k;
  int q;
  scanf("%d",&q);
  while (q --)
  {
    scanf("%lld",&k);
    l = 666 , r = 10000000000LL;
    while (l < r)
    {
      m = (l + r) >> 1;
      if (cal(m) >= k)
        r = m;
      else l = m + 1;
    }
    printf("%lld\n" , l);
  }
}

int main()
{
  work();
  return 0;
}

D

先用一次宽度优先搜索计算出各个格子着火的时间,然后在从起点出发再来一次宽度优先搜索

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
using namespace std;
#define N 1005

int n , m;
char s[N][N];
int t[N][N] , d[N][N];
int dx[] = {1 , 0 , -1 , 0} , dy[] = {0 , 1 , 0 , -1};
queue< pair<int , int> > q;

bool in(int x , int y)
{
  return x > 0 && x <= n && y > 0 && y <= m;
}

void work()
{
  int i , j , x , y;
  scanf("%d%d",&n,&m);
  memset(s , 0 , sizeof(s));
  for (i = 1 ; i <= n ; ++ i)
    scanf("%s" , s[i] + 1);

  memset(t , -1 , sizeof(t));
  for (i = 1 ; i <= n ; ++ i)
    for (j = 1 ; j <= m ; ++ j)
      if (s[i][j] == 'F')
        q.push(make_pair(i , j)) , t[i][j] = 0;
  while (!q.empty())
  {
    x = q.front().first , y = q.front().second , q.pop();
    for (i = 0 ; i < 4 ; ++ i) if (in(x + dx[i] , y + dy[i]))
      if (s[x + dx[i]][y + dy[i]] != '#' && !~t[x + dx[i]][y + dy[i]])
      {
        t[x + dx[i]][y + dy[i]] = t[x][y] + 1;
        q.push(make_pair(x + dx[i] , y + dy[i]));
      }
  }

  int ans = 1 << 30;
  memset(d , -1 , sizeof(d));
  for (i = 1 ; i <= n ; ++ i)
    for (j = 1 ; j <= m ; ++ j)
      if (s[i][j] == 'J')
        q.push(make_pair(i , j)) , d[i][j] = 0;

  while (!q.empty())
  {
    x = q.front().first , y = q.front().second , q.pop();
    if (x == 1 || x == n || y == 1 || y == m) ans = min(ans , d[x][y] + 1);
    for (i = 0 ; i < 4 ; ++ i) if (in(x + dx[i] , y + dy[i]) && (!~t[x + dx[i]][y + dy[i]] || t[x + dx[i]][y + dy[i]] > d[x][y] + 1))
      if (s[x + dx[i]][y + dy[i]] != '#' && !~d[x + dx[i]][y + dy[i]])
      {
        d[x + dx[i]][y + dy[i]] = d[x][y] + 1;
        q.push(make_pair(x + dx[i] , y + dy[i]));
      }
  }
  if (ans == 1 << 30)
    puts("IMPOSSIBLE");
  else printf("%d\n" , ans);

}

int main()
{
  int _; cin >> _;while (_--)
    work();
  return 0;
}


E

输出最小生成树最大的边....原题..用Prim的同学小心有重边...

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <cstdio>
#include <algorithm>
#define N 10005
using namespace std;
int n , m , f[N] , ans , sum;
struct edge
{
  int x ,y ,w;     
}e[N];
bool cmp(edge x, edge y)
{
  return x.w < y.w;   
}
int getf(int x) {return f[x] == x ? x : getf(f[x]); }
int main()
{
  scanf("%d%d",&n,&m);
  int i , x , y;
  for (i = 1 ; i <= n ;i ++)
    f[i] = i;
  for (i = 1 ; i <= m ;i ++)
    scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
  sort(e + 1 , e + m + 1 , cmp);
  for (i = 1 ; i <= m ;i ++)
  {
    x = getf(e[i].x) , y = getf(e[i].y);
    if (x != y)
    {
      sum ++;
      f[x] = y;
      if (sum == n - 1)
      {
        printf("%d",e[i].w);
        return 0;
      }    
    }  
  }    
  return 0;
}

F

二分答案加验证...也是原题Pie...注意一个Trick如果答案小于0.01要变成0.00,可以把所有数乘上100避免浮点运算。

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <climits>
using namespace std;
#define N 100005
#define M 500005
int n , k;
int a[N];
long long sum;
void work()
{
  int i , x; double p;
  scanf("%d%d",&n,&k);
  for (i = 1 ; i <= n ; ++ i)
    scanf("%lf" , &p) , a[i] = (int)(p * 100.0 + 1e-9) , sum += a[i];
  int l = 1 , r = 10000000 , m;
  while (r - l > 0)
  {
    m = (l + r + 1) >> 1 , x = 0;
    for (i = 1 ; i <= n ; ++ i)
      x += a[i] / m;
    if (x >= k)
      l = m;
    else r = m - 1;
  }
  if (l == 1 && sum < k) l = 0;
  printf("%.2f\n" , l / 100.0);
}

int main()
{
  work();
  return 0;
}

G

类似于高精度加法的水题...读懂题是关键

#include <iostream>
#include <string>
using namespace std;
string a,b;
int main()
{
    cin>>a;
    while (cin>>b) 
    {
       for (int i=0;i<a.length();++i)
         a[i]=(a[i]-'0'+b[i]-'0')%10+'0';
    }
    cout<<a<<endl;
} 


H

还记得上课讲过的排列变换吗...这就是换成字符串的原题...

排个序加一下就行了...

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
#define N 100005

int n , ca;
char s[N] , t[N];

void work()
{
  int i ; long long ans = 0;
  scanf("%d%s %s" , &n , s , t);
  sort(s , s + n) , sort(t , t + n);
  for (i = 0 ; i < n ; ++ i)
    ans += abs(s[i] - t[i]);
  printf("Case %d: %lld\n" , ++ ca , ans);
}

int main()
{
  int _; cin >> _;while (_--)
    work();
  return 0;
}

I

一个变种的背包问题,说实话只要想到按altitude排序一下就行了,剩下的就是裸的多重背包了,用不着什么优化。

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <cstdio>
#include <algorithm>
#define N 40005
using namespace std;
bool f[N];
int n;
struct st
{
  int h , a , c;
}s[N];
bool cmp(st x , st y)
{
  return x.a < y.a;
}

int main()
{
  int i , j , k;
  cin >> n;
  for (i = 1 ; i <= n ;i ++)
    scanf("%d %d %d",&s[i].h,&s[i].a,&s[i].c);
  sort(s + 1 , s + n + 1 , cmp); 
  f[0] = 1;
  for (i = 1 ;i <= n ;i ++ ) 
    for (k = s[i].a ; k >= 0 ;k --)
      for (j = 1 ;j <= s[i].c ;j ++)
        if (k - s[i].h * j >= 0 && f[k - s[i].h * j])
          f[k] = 1;

  for (i = 40000 ; i >= 0 ;i --)
    if (f[i])
    {
      printf("%d\n",i);
      break;
    }
  return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值