18级北航软件学院算法复习--Samshui

A 比特手链

简单模拟 判断 贪心

叶姐要想哥赠送一串比特手链,这个手链由0和1组成。想哥买了手链B,无意间得知叶姐想要同样长度的手链A。想哥囊中羞涩,只能手工调整手链。他希望最少通过以下操作进行最少步骤把B变成A。注意:A != B

 对于一个串S:
 操作1——选择下标i,j,i != j:
  ·result = S[i] & S[j]
  ·S[i] = result & S[i]
  ·S[j] = result & S[j]
 操作2——选择下标i,j,i != j:
  ·result = S[i] | S[j]
  ·S[i] = result | S[i]
  ·S[j] = result | S[j]
 操作3——选择下标i,j,i != j:
  ·result = S[i] ^ S[j]
  ·S[i] = result ^ S[i]
  ·S[j] = result ^ S[j]
 问想哥最少多少步能达成心愿。如果想哥无法达成心愿,输出-1

输入

第一个数为数据组数T

接下来2T行,第2i - 1行为手链B,第2i行为手链A

输出

对于每组数据,输出一行,最少的步骤数。特别地,如果无法达成,输出-1。

 #include <cstring>
 #include <iostream>
 #include <algorithm>
 #include <string>
 using namespace std;
 int ans;
 int main()
 {
     ios::sync_with_stdio(false);
     cin.tie(nullptr);
     int T;
     cin >> T;
     while (T--)
    {
         ans = -1;
         string A,B;
         cin >> B >> A;
         int count_0 = 0,count_1 = 0,b0 = 0,b1 = 0;
         for (int i = 0; i < A.length(); ++i)
        {
             if(A[i] != B[i])
            {
                 if(B[i] == '0')count_0++;
                 if(B[i] == '1')count_1++;
            }
             if(B[i] == '0')b0++;
             if(B[i] == '1')b1++;
        }
         if(b1 == B.length()||b0 == B.length())
             cout << "-1" << endl;
         else
             cout << max(count_0,count_1) << endl;
    }
 }

​优先选择用时少且操作多的做法


B W型串

递归 字符串

一个由括号构成的字符串称为W型串(W-string),当且仅当该串可以表示为(A)或(A)(B)的形式,其中A,B是空串或W型串。例如()、((()))、(())()等是W型串,而()()()、(()(())())等不是W型串. 给出n个由括号构成的非空字符串,判断每个串是否为W型串.

输入

第一行为一个正整数n,表示需判断的字符串个数. 接下来n行,每行一个仅由左右括号(ASCII码分别为40和41)构成的非空字符串S。0 < |S| <= 1000; n <= 100.

输出

输出n行,分别对应每个字符串是否为W型串,如果是则输出Yes,否则输出No.

 #include <cstdio>
 #include <iostream>
 #include <string>
 #include <stack>
 using namespace std;
 int n;
 bool slove(string str)
 {
     /*特殊判断:从基础部分开始*/
     if (str.empty()) return true;
     if (str[0] != '(') return false;
     if (str[str.length() - 1] != ')') return false;
     if (str[0] == '(' && str[1] == ')' && str.length() == 2)
         return true;
     
     stack<char> S;
     while (!S.empty()) S.pop();
     
     int i;
     for (i = 0; i < str.length(); i++)
    {
         if (str[i] == '(') S.push(str[i]);
         else S.pop();
         if (S.empty())
        {
             if (i == str.length() - 1) i++;
             break;
        }
    }
     if (!S.empty()) return false;//未完全匹配,不是W串
     if (i == str.length()) //完全匹配且属于仅有一部分的W串
    {
         string tmp = str.substr(1, i - 2);//向内缩进
         return slove(tmp);
    }
     //第一部分完全匹配
     else
    {
         string tmp = str.substr(1, i - 1);
         string tmpp = str.substr(i + 2);
         tmpp.erase(tmpp.length() - 1);
         return(slove(tmp) && slove(tmpp));
    }
 
 }
 int main()
 {
     ios::sync_with_stdio(false);
     cin.tie(nullptr);
     cin >> n;
     while (n--)
    {
         string S;cin >> S;
         if (slove(S)) printf("Yes\n");
         else printf("No\n");
    }
     return 0;
 }

一组示例

 8
 (((((()))))) Yes
 ))()( No
 ((((() No
 (()(())) Yes
 ((()())(()()))((()())(()()))   Yes
 (()(())()((())))() No
 ((())(()))((())(())) Yes
 (((()()(((())))))) No

C 石头剪子布

FFT string-match 模板

石头Rock剪子Scissors布Paper。想哥和叶姐又开始这个有趣的游戏。众所周知,他们俩的游戏不可能公平。他们分别给出自己出的序列(RSP分别代表石头、剪刀、布)。想哥给出一个长度为n的序列,而叶姐给出长度为m的序列。1≤m<n≤100,000。叶姐显然有特权,她可以选择跳过想哥序列的一段开头,才开始将RSP序列进行匹配,以寻求从这一位置开始最多获胜次数。请你帮叶姐求出这一次数,这就是想哥请17级吃饭的次数。显然,R胜S,S胜P,P胜R

输入

第一行为n,m,含义如上。接下来两行分别为想哥和叶姐的RSP序列。

输出

输出一行,最大获胜数

 #include <iostream>
 #include <cmath>
 #include <algorithm>
 using namespace std;
 const int N = 400005;//一般开出4倍空间
 const double PI=acos(-1.0);
 struct cp
 {
     double r,i;
     cp(){}
     cp(double _r,double _i)
    {r=_r;i=_i;}
     cp operator +(const cp a)const
    {return cp(a.r+r,a.i+i);}
     cp operator -(const cp a)const
    {return cp(r-a.r,i-a.i);}
     cp operator *(const cp a)const
    {return cp(r*a.r-i*a.i,r*a.i+i*a.r);}
     cp conj()
    {return cp(r,-i);}
 };
 int n = 1,m;
 cp f[N],g[N],omg[N],inv[N];
 void FFT_init()
 {
     for(int i = 0;i < n;i++)
    {
         omg[i] = cp(cos(2*PI*i/n),sin(2*PI*i/n));
         inv[i] = omg[i].conj();
    }
 }
 void fft(cp *a,cp *omg)
 {
     int lim = 0;
     while((1 << lim) < n) lim++;
     for(int i = 0;i < n;i++)
    {
         int t = 0;
         for(int j = 0;j < lim;j++)
             if(i >> j & 1) t |= 1 << (lim - j - 1);
         if(i < t) swap(a[i],a[t]);
    }
     for(int l = 2;l <= n;l *= 2)
    {
         int m = l / 2;
         for(cp *p = a;p != a + n;p += l)
        {
             for(int i = 0;i < m;i++)
            {
                 cp t = omg[n / l * i] * p[m + i];
                 p[m + i] = p[i] - t;
                 p[i] = p[i] + t;
            }
        }
    }
 }
 int res[N],sum[N];
 char a[N],b[N];
 int lena,lenb;
 void solve(char c)
 {
     for(int i = 0;i < lena;i++)
    {
         if(a[i] == c) f[i].r = 1;
         else f[i].r = 0;
         f[i].i = 0;
    }
     for(int i = lena;i < n;i++)
    {
         f[i].r = 0;
         f[i].i = 0;
    }
     for(int i = 0;i < lenb;i++)
    {
         if(b[i] == c) g[i].r = 1;
         else g[i].r = 0;
         g[i].i = 0;
    }
     for(int i = lenb;i < n;i++)
    {
         g[i].r = 0;
         g[i].i = 0;
    }
     fft(f,omg),fft(g,omg);
     for(int i = 0;i < n;i++) f[i] = f[i] * g[i];
     fft(f,inv);
     for(int i = 0;i < n;i++)
    sum[i] = (int)(f[i].r / n + 0.5);
     for(int i = 0;i < n;i++)    
         res[i] += sum[i];    
 }
 int main()
 {
     scanf("%d%d",&lena,&lenb);
     scanf("%s%s",a,b);
     for(int i = 0;i < lena;i++)
    {
         if(a[i] == 'S') a[i] = 'R';
         else if(a[i] == 'R') a[i] = 'P';
         else if(a[i]=='P') a[i] = 'S';
    }
     reverse(b,b + lenb);
     while(n < lena * 2 || n < lenb * 2) n <<= 1;
     FFT_init();
     solve('S');
     solve('R');
     solve('P');
     int ans = 0;
     for(int i = lenb - 1;i < lena + lenb - 1;i++)    
         ans = max(ans,res[i]);    
     printf("%d\n",ans);
     return 0;
 }

附码:FFT大数乘法

输入的第二个数flag:1表示FFT,-1表示反FFT,同时control控制进制;该代码可以处理含有负号的乘法,但是注意负数与0相乘会有负号保留,所以如果使用时WA掉,记得改一下是不是有数据卡住了

 #include <cmath>
 #include <cstdio>
 #include <cstring>
 #include <iostream>
 #include <algorithm>
 #include <string>
 #define MAXN 1000100
 using namespace std;
 const double pi = M_PI;
 const int control = 10;//控制进制
 string s1,s2;
 int N,M,K,AA,BB,l,L;
 int ans[MAXN],R[MAXN];
 int res = 0,Lim = 1;
 int i,j,k;
 struct node{
     double x,y;
     node(double xx = 0, double yy = 0){x = xx,y = yy;}
 }A[MAXN],B[MAXN];
 node operator *(node J,node Q)
 { return node(J.x * Q.x - J.y * Q.y,J.x * Q.y + J.y * Q.x);}
 node operator +(node J,node Q)
 { return node(J.x + Q.x , J.y + Q.y);}
 node operator -(node J,node Q)
 { return node(J.x - Q.x , J.y - Q.y);}
 inline void FFT(node *J,double flag)
 {
     for (i = 0; i < Lim; ++i)
         if(i < R[i]) swap(J[i],J[R[i]]);
     for (j = 1; j < Lim;j <<= 1) {
         node T(cos(pi / j),flag * sin(pi / j));
         for (k = 0; k < Lim; k += (j << 1)) {
             node t(1,0);
             for (l = 0; l < j; ++l,t = t * T) {
                 node Nx = J[k + l];
                 node Ny = t * J[k + j + l];
                 J[k + l] = Nx + Ny;
                 J[k + j + l] = Nx - Ny;
            }
        }
    }
 }
 inline void init()
 {
     memset(A,0,sizeof(A));
     memset(B,0,sizeof(B));
     memset(ans,0,sizeof(ans));
     memset(R,0,sizeof(R));
     N = M = K = AA = BB = l = L = 0;
     res = 0,Lim = 1;
 }
 int main()
 {
     while (cin >> s1 >> s2)
    {
         init();
         bool y1 = true,y2 = true;
         for(i=s1.length()-1;i>=1;i--)A[AA++].x=s1[i]-48;
         for(i=s2.length()-1;i>=1;i--)B[BB++].x=s2[i]-48;
         int L1 = s1.size(),L2 = s2.size();
         if(s1[0] == '-') y1 = false,L1--;
         else A[AA].x = s1[0] - 48;
         if(s2[0] == '-') y2 = false,L2--;
         else B[BB].x = s2[0] - 48;
         while (Lim < L1 + L2) Lim <<= 1,L++;
         for (i = 0; i <= Lim; ++i)
             R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));
         FFT(A,1);
         FFT(B,1);
         for (i = 0; i <= Lim; ++i)
             A[i] = A[i] * B[i];
         FFT(A,-1);
         for (i = 0; i <= Lim; ++i)
        {
             ans[i] += (int)(A[i].x / Lim + 0.5);
             if(ans[i] >= control)
            {
                 ans[i + 1] += ans[i] / control;
                 ans[i] %= control;
                 Lim += (i == Lim);
            }
        }
         while (!ans[Lim] && Lim >= 1)Lim--;
         Lim++;
         if(y1 != y2)cout << '-';
         while (--Lim >= 0)cout << ans[Lim];
         cout << endl;
         s1.clear();s2.clear();
    }
     return 0;
 }

D 芸如的入学测试

区间和 前缀和 取余操作

校长的问题是这样的:在一个长度为N的数字序列A,有Q组询问,每组询问给定lrlr,请求出A[l]+A[l+1]+...+A[r]的值。由于这个结果可能很大,最终的结果要对10007取模,即取余数

输入

多组数据输入,数据组数不超过10

第一行是一个数字N,Q,表示序列A中元素的个数和询问组数。(0<NQ≤1e6)第二行是N个整数,第i个整数A[i]表示序列A中的第i个元素,保证均为非负整数,且在INT范围内。接下来Q行,每行是两个用空格分隔的整数l和r(保证l和r不会超出序列A下标的范围,且lr)。

注意序列A的下标从1开始

输出

对于每组数据,每个询问输出一行,为和值

数据量较大,读入请勿用过慢读入方式

 #include<iostream>
 #include<cstdio>
 #include<cstring>
 #include<algorithm>
 #define MOD 10007
 using namespace std;
 const int maxn = 1e6 + 1;
 int N, Q;
 int sum[maxn];
 int main()
 {
  while(scanf("%d%d", &N, &Q) != EOF)
  {
  memset(sum, 0, sizeof(sum));
  int num;
  for(int i = 0; i < N; ++ i)
  {
  scanf("%d", &num);
  sum[i + 1] = (sum[i] % MOD + num % MOD) % MOD;
  }
  int l, r;
  for(int i = 1; i <= Q; ++ i)
  {
    scanf("%d%d", &l, &r);
             printf("%d\n",(sum[r] - sum[l - 1] + MOD) % MOD);
  }
  }
  return 0;
 }

E SkyLee的艾露猫

斐波那契 递推

艾露猫很可爱,但是寿命只有短短的20年,艾露猫在出生后2年成年,且成年时每对艾露猫都会在年初产下一对小猫,艾露猫成年10年后进入老年,老年持续8年后遗憾地去世。现在SkyLee得到了一对可爱的艾露猫,他希望艾露猫越多越好,这样打起龙来就很轻松了。假设艾露猫很传统,都是一夫一妻制,并且生出的小猫也正好为一对,若现在为第1年年初,那么第n年SkyLee一共能有多少只可爱的艾露猫呢?

输入

第一个数为数据组数T接下来T行,每行1个整数n(保证艾露猫数量不超过int)

输出

对于每组数据,输出一行,为第n年艾露猫的数量

注意条件的区分:成年 —— 老年 —— 死亡

 #include<cstdio>
 long long A[100005] = {0,2,2,4,6,10,16,26};
 int main()
 {
     /*在开始计算时不考虑死亡的猫以及其带来的影响*/
     for(int i = 8;i < 51;i++)
    {
         if(i < 13) A[i] = A[i - 1] + A[i - 2];
         else A[i] = A[i - 1] + A[i - 2] - A[i - 12];
    }
     int T;scanf("%d",&T);
     while(T--)
    {
         int n;scanf("%d",&n);
         /*此时,如果有猫已经死亡,则直接去除死亡部分(以及死亡牵连的增加)*/
         if(n > 20) printf("%lld\n",A[n] - A[n - 20]);
         else printf("%lld\n",A[n]);
    }
 }

F SkyLee在GameStop

递归调用函数 汉诺塔变形 字符串输出

SkyLee有一天逛街的时候看到一家新开业的GameStop,里面卖各种各样的游戏。商店里所有的游戏都按游戏名的字典序从小到大排列好了,小的在里面,大的在外面。SkyLee想要把所有的游戏都试玩(买不起游戏只能看看),但是有些问题:

  • 游戏只能从展示架的一侧拿出来

  • SkyLee只能拿1个游戏试玩

  • 为了不被商店老板发现蹊跷,SkyLee把游戏光盘放回去的时候总要保证每个展示架的游戏仍然按照字典序从小到大排列(小的在里面,大的在外面)

  • SkyLee虽然没钱但是不可能偷游戏,离开时不能拿着游戏

  • SkyLee发现了两个空的展示架可以放游戏

SkyLee给摆放有游戏的那个展示架编号1,空的编号2和3。假设SkyLee拿游戏、放游戏和试玩游戏都需要时间,现在由你来帮SkyLee提出一个最快的把所有游戏都试玩完的方案吧。在同样快的试玩方案中,SkyLee会第一时间试玩他拿到的新游戏,然后尽量把字典序更小的游戏放在编号大的展示架上

输入

多组数据每组数据1个数n表示游戏的数量(1 ≤ n ≤ 10)

输出

对于每组数据,输出把所有游戏都试玩完的最快方案,按以下要求: 拿出游戏输出一行get game from board i,其中i是展示架的编号。 放回游戏输出一行put game to board i,其中i是展示架的编号。  试玩游戏输出一行playing。 离开商场输出一行leave

输入样例

 2

输出样例

 get game from board 1
 playing
 put game to board 2
 get game from board 1
 playing
 put game to board 3
 leave

样例解释

1号展示架上放了两个游戏,字典序从小到大标为A,B。首先SkyLee拿出B并试玩,然后放回到2号展示架上。然后SkyLee拿出A并试玩,这样他就把所有的游戏都玩过一遍了。SkyLee需要在同样快的方案里,把字典序更小的放到编号更大的展示架上。所以他玩完A后放回到3号展示栏上,然后就离开GameStop了

 #include<iostream>
 using namespace std;
 void move_(char from,char to)
 {
  cout << "get game from board " << from << endl;
  cout << "playing" << endl;
  cout << "put game to board " << to << endl;
 }
 void move1(char from, char to)
 {
  cout << "get game from board " << from << endl;
  cout << "put game to board " << to << endl;
 }
 void hanoi1(int n, char from, char mid, char to)
 {
  if (n == 1)
    {
  move1(from, to);
  return;
  }
  hanoi1(n - 1, from, to, mid);
  move1(from, to);
  hanoi1(n - 1, mid, from, to);
 }
 void hanoi(int n, char from, char mid, char to)
 {
  if (n == 1)
    {
  move_(from, to);
  return;
  }
  hanoi(n - 1, from, to, mid);
  move_(from, to);
  hanoi1(n - 1, mid, from, to);
 }
 int main()
 {
     ios::sync_with_stdio(false);
     cin.tie(nullptr);
  int n;
  while (cin >> n)
    {
  if (n > 2)
  {
  hanoi(n - 2, '1', '3', '2');
  move_('1', '3');
  move_('1', '1');
  }
  else if (n == 2)
  {
  move_('1', '2');
  move_('1', '3');
  }
  else
  move_('1', '3');
  cout << "leave" << endl;
  }
  return 0;
 }

Tower of Hanoi

 #include<iostream>
 using namespace std;
 void hanoi(char a,char b,char c,int n)
 {
     //直接移动到C柱,输出
     if (n == 1) cout << a << " -> " << c << endl;  
     else
    {
         hanoi(a,c,b,n - 1);  //把上面n-1个移到B柱
         cout << a << " -> " << c << endl;  //输出
         hanoi(b,a,c,n - 1);  //把剩下n-1个从B柱移到C柱
    }
 }
 int main()
 {
     int n;
     cin >> n;
     hanoi('A','B','C',n);
 }
 

G ModricWang's Real QuickSort Query

快排思想 递归 模拟

快排的一个基础操作就是划分(partition) ,就是将当前的数组分为前后两个部分。一种较为经典的partition 方法是,将数组中处于中间位置(注意,只和位置有关,和大小无关)的元素作为分隔元素,然后将小于它的元素放到左侧,大于它的元素放到右侧,然后对左右两侧分别进行递归操作。在此题中为了统一,如果数组长度为偶数,取靠后的一个作为分隔元素。需要注意的是,快排的划分是一种原地划分,而且左右两边的长度是未知的,因此它在操作时采取以下的一种方式:

  1. 设数组为arr[n] ,元素从0开始存储

  2. 令i = 0,j = n − 1,mid = arr[n / 2]

  3. 如果i ≤ j,转到4,否则转到步骤7

  4. 如果arr[i] < mid,i++ ,重复执行直到arr[i] ≥ mid

  5. 如果arr[j] > mid,j−− ,重复执行直到arr[j] ≤ mid

  6. 如果i ≤ j, 交换arr[i] 和arr[j],i++,j−− , 转到步骤4

  7. 退出

进行第一次递归时,数组被分为左右两个部分:[0,i)和[i,n),其中i就是执行partition时的i,进行第二层的递归时,数组总共被分为4个部分。现在ModricWang想让你输出第二层递归时从左往右的第二部分的元素

输入

第一个数为数组长度n,16  ≤ n  ≤  106

第二行n个整数,为待排序的元素,保证在int范围内且不重复

输出

输出一行,第二层递归时从左往右的第二部分的元素 ,数据保证这一部分不为空

输入样例

 16
 10 6 2 7 14 4 1 13 8 15 5 3 9 11 12 16

输出样例

 7 6 8

HINT

原数据

 10 6 2 7 14 4 1 13 8 15 5 3 9 11 12 16

第一次递归

 3 6 2 7 5 4 1 8 / 13 15 14 10 9 11 12 16

第二次递归

3 1 2 4 5 / 7 6 8 / 9 / 15 14 10 13 11 12 16
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
void Q_sort(vector<int> A,int left_,int right_,int step)
{
    int i = left_,j = right_,mid = A[(left_ + right_ + 1) / 2];
    while(i <= j)
    {
        while(A[i] < mid)i++;
        while(A[j] > mid)j--;
        if(i <= j)
        {
            swap(A[i],A[j]);
            i++,j--;
        }
    }
    if(step == 2)/*递归层数达到,输出*/
    {
        for(;i <= right_;i++) printf("%d ",A[i]);
        printf("\n");
        exit(0);
    }
    if(left_ < j) Q_sort(A,left_,j,step + 1);
    if(right_ > i) Q_sort(A,i,right_,step + 1);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;cin >> n;
    vector<int> A(n);
    for(int i = 0;i < n;i++) cin >> A[i];
    Q_sort(A,0,n - 1,1);
}

Quick Sort

int partition(int arr[], int left, int right)//划分
{
    int i = left + 1 ;
    int j = right;
    int temp = arr[left];
    while(i <= j)
    {
        while (arr[i] < temp) i++;
        while (arr[j] > temp ) j--;
        if (i < j)
            swap(arr[i++], arr[j--]);
        else i++;
    }
    swap(arr[j], arr[left]);
    return j;
}
void quick_sort(int arr[], int left, int right)//递归调用
{
    if (left > right) return;
    int j = partition(arr, left, right);
    quick_sort(arr, left, j - 1);
    quick_sort(arr, j + 1, right);
}
int main()
{
    int a[] = {1,4,6,2,3,5,8,9,11};
    quick_sort(a,0,sizeof(a)/sizeof(int));
    for(int i = 0;i < sizeof(a)/sizeof(int);i++)
        cout << a[i] << " ";
}

SkyLee的图书整理

直接映射表 桶统计法 暴力模拟

SkyLee在图书馆帮忙整理图书,同学们在借阅时都比较随意,导致SkyLee面前有一堆顺序混乱的书。图书馆管理员要求SkyLee整理出某本书的个数,可是面对这么多的书,SkyLee实在不知道如何下手,聪明的你能帮帮他么?

输入

多组数据输入,第一行两个数字,分别为书的总数量n,查询次数t;第二行n个数字,为n本书的编号,第三行t个数字,为要查询的书的编号p

输出

对于每组数据,输出一行,为查询的每种书的本数number

输入样例

5 3
2 2 3 1 3
1 2 3

输出样例

1 2 2

数据范围

1 ≤ n , t ≤ 100,000,number在int范围内。对于10%的数据, 1 ≤ n,t ≤ 1000

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
const int N = 100005;
using namespace std;
int store[N];//桶
int main()
{
    int n,t;
    while(~scanf("%d%d",&n,&t))
    {
        memset(store,0,sizeof(store));
        for(int i = 0;i < n;i++)
        {
            int x;
            scanf("%d",&x);
            store[x]++;
        }
        int ask;
        for(int i = 0;i < t - 1;i++)
        {
            scanf("%d",&ask);
            printf("%d ",store[ask]);
        }
        scanf("%d",&ask); printf("%d\n",store[ask]);
    }
}

I 天秤的烦恼

STL:unique函数 | 暴力 + 剪枝

简化题意

给出一个数组,再给出一个数组里面的数字,求该数字在数组里面为第几大的数(重复的算一个数)

输入

多组数据输入,第一个数为晶体管的数量n (n <= 1e6) 接下来一行,共计n个整数,为晶体管的压制力x。保证x满足-232 <= x <= 232-1,且之间用空格隔开,最后一行是要拆除的晶体管的压制力。

输出

对于每组数据,输出一行,为晶体管的压制力在所有晶体管中为第几大。

输入样例

6
1 2 2 3 3 4
3

输出样例

2

注意:第一种为Unique解法,第二种为暴力解法 以及数据选择long long

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
vector<long long>nums;
void init(){nums.clear();}
int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        init();
        for(int i = 0;i < n;i++)
        {
            long long t;
            scanf("%lld", &t);
            nums.push_back(t);
        }
        vector<long long>::iterator it_1 = nums.begin();
        vector<long long>::iterator it_2 = nums.end();
        vector<long long>::iterator new_end;
        sort(it_1,it_2,greater<long long>());
        new_end = unique(it_1,it_2);
        nums.erase(new_end,it_2);
        int tobeFind;
        scanf("%d", &tobeFind);
        for(int i = 0;i < nums.size();i++)
        {
            if(nums[i] == tobeFind)
            {
                printf("%d\n", i + 1);
                break;
            }
        }
    }
    return 0;
}
long long a[1000005];
bool cmp(long long a,long long b){return a > b;}
int main()
{
	int n;/*手打的unique顶好*/
	while(~scanf("%d",&n))
    {
		int i,ans = 1,num;
		for(i = 0;i < n;i++) scanf("%lld",&a[i]);
		scanf("%d",&num);
		sort(a,a + n,cmp);
		for(i = 1;i < n;i++)
		{
			if(a[i] < a[i - 1]) ans++;
			if(a[i] == num) break;
		}
		printf("%d\n",ans);
	}
}

J 女娲加农炮

优先队列STL 模拟

现在芸如想化核裂变为核聚变,从而让女娲加农炮的火力再上一层楼,已知有N种不同的原子核(这里请忽略自然界原子核的种类上限),第i种原子核的重量为a[i]。将两种原子核聚合在一起,消耗的能量等于两种原子核的重量之和。在经过n-1次聚合之后,聚合完成。芸如想使整个聚合过程消耗的能量最小,请求出这个最小的能量值

输入

多组数据输入,第一个数为原子核的种类N(N <= 1e6)接下来N个整数,代表N种原子核的重量。(在int范围内并用空格隔开)

输出

对于每组数据,输出一行,为聚合过程能量消耗的最小值。(保证结果在int范围内)

输入样例

4
1 2 3 4

输出样例

19
#include<iostream>
#include<queue>
using namespace std;
priority_queue<int,vector<int>,greater<int>> weight;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int N;
    while(cin >> N)
    {
        while(!weight.empty()) weight.pop();//INIT
        for(int i = 0;i < N;i++)
        {
            int w;cin >> w;
            weight.push(w);
        }
        long long all = 0;
        while(weight.size() > 1)
        {
            int temp = 0;
            temp += weight.top();
            weight.pop();
            temp += weight.top();
            weight.pop();
            all += temp;
            //attentation not push all but temp!!!
            weight.push(temp);
        }
        cout << all << endl;
    }
}

K 第k顺序统计量

STL:nth_element函数

给定3个整数A, B, C, 和数组第一个数a[1],数组a由如下方式得到,询问a中第k小的数是多少

for(int i = 2; i <= 3000000; ++i)
a[i] = ((1LL * a[i - 1] * A ^ B) + C) % 1000000007;

输入

多组组数据,每组数据一行,数据组数不超过7,每行5个整数A, B, C, a[1], k (保证五个数为int范围内正整数且k在3000000范围内)

输出

对于每组数据,输出一行,第k小的数

#include<cstdio>  
#include<algorithm>  
const int MAXN = 3000002;  
int a[MAXN],A,B,C,k;  
using namespace std;  
/*
    对给定范围[first,last)内的元素进行重新布置.方法是,nth位置的元素放置的值就是把所有元素排序后在nth位置的值.把所有不大于nth的值放到nth的前面,把所有不小于nth的值放到nth后面.对给定范围内的元素"排序"
*/
int main()  
{  
    while(scanf("%d%d%d%d%d",&A,&B,&C,&a[1],&k) == 5)
    {  
        for(int i = 2;i <= 3000000;i++)  
            a[i] = ((1LL * a[i-1] * A ^ B) + C) % 1000000007;  
        nth_element(a + 1,a + k,a + 3000001);  
        printf("%d\n",a[k]);  
    }  
}

做法二:Qsort


L 女娲加农炮II

J题的进阶

将三种原子核聚合在一起,消耗的能量等于三种原子核的重量之和。在经过多次聚合之后,聚合完成。(如果原子核的数量小于3则不进行进一步的聚合)芸如想使整个聚合过程消耗的能量最小,请求出这个最小的能量值

priority_queue<int,vector<int>,greater<int>> weight;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int N;
    while(cin >> N)
    {
        while(!weight.empty()) weight.pop();
        for(int i = 0;i < N;i++)
        {
            int w; cin >> w;
            weight.push(w);
        }
        long long all = 0;
        for(int i = N;i >= 3;i = i - 2)//注意跨步为2
        {
            int temp = 0;
            temp += weight.top();weight.pop();
            temp += weight.top();weight.pop();
            temp += weight.top();weight.pop();
            all += temp;//注意temp和all的区别
            weight.push(temp);
        }
        cout << all << endl;
    }
}

M SKyLee的补番计划

多重背包

SkyLee最近想要补一下番,但是学业繁重没有时间全都补完,所以他需要选出一些番来补。SkyLee想补的番一共有n部,每部的集数都不同,每集的长度也不同(别和我说标准番剧24分钟,还有剧场版呢orz)并且他对于每部番的喜好程度不同,即看每部番得到的愉悦度不同,现在SkyLee一共有T分钟来补番,那么他最多能有多愉(xing)悦(fen)呢?

输入

多组数据输入,第一行两个整数n,T(0 <= n <= 500,0 <= T <= 1e5)

接下来n行,每行三个整数,分别为集数m,每集时长t,看每集得到的愉悦度v(0 < m <= 1000,0 < t <= 200,0 <= v <= 1000)

输出

对于每组数据,输出一行,为最大愉悦度

输入样例

2 10
3 3 4
2 4 5 

输出样例

13
#include <cstdio>
int n,k,v,a,b,total,c,w,m,dp[100005],tc,tw;
int main()
{
   while(scanf("%d%d",&n,&v) != EOF)
   {
   	    for(int i = 0;i <= v;i++) dp[i] = 0;
        for(int i = 0;i < n;i++)
        {
        	scanf("%d%d%d",&m,&c,&w);
        	for(k = 0;1 << (k + 1) <= m;k++)
        	{
        		tc = c * (1 << k);
        		tw = w * (1 << k);
        		for(int j = v;j >= tc;j--)
                {
        			a = dp[j - tc] + tw;
        			if(dp[j] < a) dp[j] = a;
        		}
        	}
            k = m - (1 << k) + 1;
            tc = c * k; tw = w * k;
            for(int j = v;j >= tc;j--)
            {
                a = dp[j - tc] + tw;
                if(dp[j] < a) dp[j] = a;
            }
        }
        printf("%d\n",dp[v]);
    }
}

背包模板

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 100005;
int dp[MAXN],Cost[MAXN],Amo[MAXN],Fun[MAXN];
int nvalue,nkind;
void ZeroOnepack(int cost,int weight)
{
    for(int i = nvalue;i >= cost;i--)
        dp[i] = max(dp[i],dp[i - cost] + weight);
}
void Completepack(int cost,int weight)
{
    for(int i = cost;i <= nvalue;i++)
        dp[i] = max(dp[i],dp[i - cost] + weight);
}
void Mulpack(int cost,int weight,int amount)
{
    if(cost * amount >= nvalue) Completepack(cost,weight);
    else
    {
        int k = 1;
        while(k < amount)
        {
            ZeroOnepack(k * cost,k * weight);
            amount -= k;
            k <<= 1;
        }
        ZeroOnepack(amount * cost,amount * weight);
    }
}
void INIT()
{memset(dp,0,sizeof(dp));}
int main()
{
    while(~scanf("%d%d",&nkind,&nvalue))
    {
        INIT();
        for(int i = 0;i < nkind;i++)
            scanf("%d%d%d",&Amo[i],&Cost[i],&Fun[i]);
        for(int i = 0;i < nkind;i++)
            Mulpack(Cost[i],Fun[i],Amo[i]);
        printf("%d\n",dp[nvalue]);
    }
}

N RMQ问题

dp动态规划 RMQ算法

问题

给定长度为N的序列,M个询问,每次询问两个数字A,B,要求求出属于A到B这段区间内的最大数

输入

一个整数N表示数字的个数,接下来一行为N个数。第三行读入一个M,表示你看完那串数后需要被提问的次数,接下来M行,每行都有两个整数A,B。

HINT

对40%:N ≤ 6000,M ≤ 500
对100%:N ≤ 200000,M ≤ 10000,
所有数均在int范围内
#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 25
#define N 200005
using namespace std;
int n,m;
int a[N],Log[N];
int f[N][M];//dp数组,f[i][j]表示以i为起点,连续2^j个数中的最大值
void GetLog()
{
	int i; Log[1] = 0;
	for(i = 2;i <= n + 1;++i) Log[i] = Log[i / 2] + 1;
}
void RMQ()
{
	int i,j;
	for(i = 1;i <= n;++i) f[i][0] = a[i];
	for(j = 1;(1 << j) <= n;++j)
        for(i = 1;i + (1 << (j - 1)) <= n;++i)
            //转移方程
            f[i][j] = max(f[i][j - 1],f[i + (1 << (j - 1))][j - 1]);
}
int main()
{
	int l,r,i,k,ans;
	scanf("%d",&n);
	for(i = 1;i <= n;++i) scanf("%d",&a[i]);
	GetLog();RMQ();
	scanf("%d",&m);
	for(i = 1;i <= m;++i)
	{
		scanf("%d%d",&l,&r);
		k = Log[r - l + 1];
		ans = max(f[l][k],f[r - (1 << k) + 1][k]);
		printf("%d\n",ans);
	}
	return 0;
}

O 凯恩·血蹄--烽火戏诸侯

贪心 简单模拟

牛头人酋长为了防御狗头人酋长的进攻,决定从营地到前线一条直线上,修筑一些烽火台来更好地传递消息,他让牛头人考察了许多地点,来决定修筑烽火台的位置。 N只小牛头人带回来了N条考察结果,分别为在距离前线Ai处,若是修烽火台,每修高一米,就要消费Pi 币,同时每修高一米,就使自己被点燃时,在多一单位距离内被看到。当然,只有一个烽火台看到前面有烽火台被点燃时,才会点燃自己而往下传递消息。现在牛头人酋长要从距离前线最近的烽火台,将消息传到距离前线最远的烽火台(这两座烽火台一定要修建),同时呢,也要尽可能少花点钱。 可是麻烦的是,有些小牛头人考察了同一个地点,甚至在同一地点修烽火台他们的价格都可能不同,这使本来就繁多的数据更乱了,于是牛头人酋长找到你,希望你计算出最少花费是多少。

输入

第一行一个数N,表示N个修烽火台位置,接下来N行,每行两个整数Ai,Pi,表示距离前线距离和单位高度(或警示范围)造价

输出

一个数,即最小造价

输入样例

6
1 100000
100 1000
20 80000
40 60000
60 40000
80 100000

输出样例

6300000

在1处修高度19的烽火台,20处修高度20的烽火台,40处修高度20的烽火台,60处修高度40的烽火台,80处不修烽火台,100处修高度为0的烽火台

对于100%的数据,0 < N <= 200000,0 <= Ai <= 100000000,0 <= Pi <= 200000000

注意:修塔必须按照顺序来,不能看到范围就认为可以圆形考虑

按理说按照距离之差确定花费,所以如果有相同位置报价不同就按照花费从小到大排序,位置一样的话,距离差为0,花费为0,千万别桶排去重,会MLE的

#include<iostream>
#include<algorithm>
#include<cstdio>
#define LL long long
#define MAXN 100000005
#define MAX_ 200005
using namespace std;
typedef struct tower{
    LL pos;
    LL cost;
}TOW;
bool cmp(TOW a,TOW b)
{
    if(a.pos == b.pos) return a.cost < b.cost;
    return a.pos < b.pos;
}
TOW C[MAX_];
LL N,Ai,Pi,now = 1;
LL now_cost,now_pos,ans;
int main()
{
    scanf("%lld",&N);c++
    for(LL i = 0;i < N;i++)
        scanf("%lld%lld",&C[i].pos,&C[i].cost);
    sort(C,C + N,cmp);
    now_cost = C[0].cost;
    now_pos = C[0].pos;
    for(int i = 1;i < N;i++)
    {
        ans += now_cost * (C[i].pos - now_pos);
        if(now_cost > C[i].cost) now_cost = C[i].cost;
        now_pos = C[i].pos;
    }
    printf("%lld",ans);
}

P 树形DP初步-二叉树

dfs 递归 树形动态规划

题意

最长链为这棵二叉树中一条最长的简单路径,即不经过重复结点的一条路径。可以容易证明,二叉树中最长链的起始、结束结点均为叶子结点。现给出一棵N(N<=100000)个结点二叉树,问这棵二叉树中最长链的长度为多少,保证了1号结点为二叉树的根

输入

输入第1行为包含了一个正整数N,为这棵二叉树的结点数,结点标号由1至N。接下来N行,这N行中的第i行包含两个正整数l[i], r[i],表示了结点i的左儿子与右儿子编号。如果l[i]为0,表示结点i没有左儿子,同样地,如果r[i]为0则表示没有右儿子。

输出

输出包括1个正整数,为这棵二叉树的最长链长度。请注意,链长定义为经过的边的个数。

#include<iostream>
#include<algorithm>
#include<cstdio>
const int MAXN = 100005;
using namespace std;
int length[MAXN],le[MAXN],ri[MAXN];
void dfs(int x)
{
    if(x != 0)
    {
        dfs(le[x]);
        dfs(ri[x]);
        //要加起来
        length[x] = max(length[le[x]],length[ri[x]]) +1;
    }
    else length[x] = 0;
}
int Llink[MAXN];
int main()
{
    int N; scanf("%d",&N);
    for(int i = 1;i <= N;i++) scanf("%d%d",&le[i],&ri[i]);
    dfs(1);
    int max_ = -1;
    for(int i = 1;i <= N;i++)
    {
        Llink[i] = length[le[i]] + length[ri[i]] + 1;
        if(Llink[i] > max_) max_ = Llink[i];
    }
    printf("%d\n",max_ - 1);//边 = 点 - 1
}

Q 树形DP初步-真树

dp 条件分析

新年到了,白兔家族要搞大大的聚会。但是并不是每只白兔都是同一辈分的,于是便有一棵以老白兔为根的家族树。每只白兔都有它们自己唯一的整数编号(范围在1到N之间),并且对应一个参加聚会所得的开心值。为了使每个参加聚会的白兔都巨开心,老白兔想让每只白兔和他的上一代白兔不会同时参加聚会,求参加聚会的白兔获得的最大总开心值。

输入

输入的第一行是一个整数N,1<= N <= 6000,以下的N行是对应的N个白兔的开心值(开心值是一个从-128到127之间的整数),接着是白兔的家族树,树的每一行格式如下: 每行输入一对整数L,K。表示第K个白兔是第L个白兔的上一代。 输入以0 0表示结束

输出

参加聚会的白兔获得的最大总开心值

注意:由于快乐值可以为负,所以可以选择一个白兔不去,那么这个时候他的孩子们就可以去了,这个条件为dp数组的第二维,白兔编号为第一维

#include<iostream>
#include<algorithm>
#include<cstdio>
const int MAXN = 6005;
using namespace std;
int dp[MAXN][2];
int par[MAXN],visited[MAXN];
int n;
void dfs(int x)
{
    //寻找x的子代
    for(int i = 1;i <= n;i++)
        if(visited[i] == 0 && par[i] == x)
        {
            dfs(i);
            //更新x的欢乐dp
            dp[x][0] += max(dp[i][1],dp[i][0]);
            dp[x][1] += dp[i][0];
        }
}
int main()
{
    int l,k,root;
    scanf("%d",&n);
    for(int i = 1;i <= n;i++) scanf("%d",&dp[i][1]);
    while(scanf("%d%d",&l,&k) != EOF && (l != 0 && k != 0))
        par[l] = k;
    for(int i = 1;i <= n;i++)//找root
        if(par[i] == 0) root = i;
    dfs(root);
    printf("%d\n",max(dp[root][0],dp[root][1]));
}

R Bamboo和"Coco"

模拟

亡灵们排成一队来领花瓣。每个亡灵都至少有一个花瓣,但是得保证若某个亡灵要比它邻近(前或后)的亡灵的思念值高,则其获得花瓣也要更多,但是上一个亡灵节收集到的花瓣就是这么多,所以主办方希望能分发尽量少的花瓣。请问主办方最少要准备多少花瓣才够发?

输入

多组输入,第一个数为数据组数n(0<n<=1e6),接下来一行n个数,第i个数代表第i个人的思念值(0<i<1e7)数据量较大,建议使用scanf/printf

输出

对于每组数据,输出一个行,为需要的最少的花瓣

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_ = 1e6 + 5;
int left_[max_],right_[max_];
int missing[max_];
int main()
{
    int n,x;
    while(~scanf("%d",&n))
    {
        //数据0 ~ n - 1
        for(int i = 0;i < n;i++) scanf("%d",&missing[i]);
        for(int i = 0;i < n;i++) left_[i] = right_[i] = 1;
        for(int i = 1;i < n;i++)
            if(missing[i] > missing[i - 1]) left_[i] = left_[i - 1] + 1;
        for(int i = n - 2;i >= 0;i--)
            if(missing[i] > missing[i + 1]) right_[i] = right_[i + 1] + 1;
        int ans = 0;
        for(int i = 0;i < n;i++) ans += max(left_[i], right_[i]);
        printf("%d\n",ans);
    }
}

S ModricWang的水系法术

MaxFlow最大流 dinic

ZKx的魔法面板上有N个点,标号为1~N。ModricWang可以通过魔法让点a和点b之间能通过流量为c的水流。当ModricWang完成M次施法后,ZKx会从点1注入无穷多的水流,并引导它们向点N流动。请你帮ModricWang算出,点N处最多会收到流量为多少的水流

输入

第一行两个整数,N和M,N为点数,M为边数,点的标号为1~N,接下来M行,每行三个整数a, b, c, 表示边的两个端点和可以通过的流量的最大值。水流的方向没有限制。保证没有重边自环

1≤N≤1000;1≤M≤100000;1≤a,b≤N,a≠b;1≤c≤10

输出

输出一个整数,点N处会收到的流量的最大值

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1005;
const int INF = 0x7f7f;
int n,m;					//n个点m条边
int S,T;					//源点、汇点
int level[N];				//存储结点分层层数
struct Edge
{
    int cap;
    int flow;
}edge[N][N];
bool bfs()
{							//构造层次网络
    memset(level,0,sizeof(level));
    queue<int> Q;Q.push(S);
    level[S] = 1;
    while(!Q.empty())
    {
        int x = Q.front();
        Q.pop();
        for(int y = 1;y <= n;y++)
        {
            if(!level[y] && edge[x][y].cap > edge[x][y].flow)
            {
                level[y] = level[x] + 1;
                Q.push(y);
            }
        }
    }
    return level[T] != 0;
}
int dfs(int x,int cp)		//计算可增加流量
{				
    if(x == n) return cp;
    int flow = cp;			//记录从x到t的最小残量
    for(int y = 1;y <= n;y++)
    {
        if(level[x] + 1 == level[y])
        {
            if(edge[x][y].cap > edge[x][y].flow)
            {
                int minn = min(flow,edge[x][y].cap - edge[x][y].flow);
                int newFlow = dfs(y,minn);
                edge[x][y].flow += newFlow;
                edge[y][x].flow -= newFlow;
                flow -= newFlow;
            }
        }
        if(flow == 0) break;
    }
    return cp - flow;
}
int dinic()
{
    int flow = 0;
    int tf = 0;
    while(bfs())
        while(tf = dfs(1,INF)) flow += tf;        
    return flow;
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(edge,0,sizeof(edge));
    while(m--)
    {
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        edge[x][y].cap += w;//便于处理重边
        edge[y][x].cap += w;//无向图!!!
    }
    S = 1,T = n;
    printf("%d\n",dinic());
    return 0;
}

T 中等·AlvinZH的青春记忆I

条件递推 dp

万兽山中的众多凶兽围绕神兽四周环绕分布,AlvinZH可以任意地选择凶兽击杀并取得其宝物,经体内神魂提醒,得知若有两个相邻的凶兽被击杀,神兽就会苏醒。为了不惊醒神兽,同时取得宝物的价值总和最大,AlvinZH必须有选择地取得宝物,你来帮帮他吧!

输入

输入包含多组数据,每组数据第一行,为万兽山中凶兽的数量 n(1 ≤ n ≤ 105);接下来一行包含n个正整数,表示凶兽对应守护的宝物的价值 v(1 ≤ v ≤ 104)。

注:宝物价值的输入顺序即代表凶兽环绕分布的相对顺序,举个栗子,若n=3,则有凶兽①②③围绕神兽环形分布,其中①②、②③,③①相邻。

输出

对于每组数据,输出一行,为AlvinZH所能取得的最大宝物价值总和。答案在int范围内。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define LL long long
const int MAX_N = 100005;
using namespace std;
LL v[MAX_N],dp[MAX_N];
int n;
/*dp[i] = max(dp[i − 1],dp[i − 2] + V[i])*/
/*别忘了环形*/
LL solve(int left,int right)//规定区间
{
    memset(dp,0,sizeof(dp));
    dp[left] = v[left];
    dp[left + 1] = max(v[left],v[left + 1]);
    for(int i = left + 2;i <= right;i++)
        dp[i] = max(dp[i - 1],dp[i - 2] + v[i]);
    return dp[right];
}
int main()
{
    while(~scanf("%d",&n))
    {        
        for(int i = 1;i <= n;i++) scanf("%lld",&v[i]);
        if(n == 1) printf("%lld\n",v[1]);
        //对比两种情况
        else printf("%lld\n",max(solve(1,n - 1),solve(2,n)));
    }
}

U 中等·AlvinZH的青春记忆II

二分答案

龙王苏醒后,恢复全部力量需要一段时间,为了安全,龙王在周围布置了一道由 nn 片龙鳞组成的保护屏障,每片龙鳞屏障都有一定的承受伤害能力,需要把所有龙鳞屏障破除,才能真正与龙王交战。每个普通混血种每秒钟可对一片龙鳞造成1个单位伤害。另外,校长昂热为S级混血种路明非带来了屠龙武器——七宗罪,路明非使用七宗罪每秒钟可对一片龙鳞造成k个单位伤害

作为菜鸟的AlvinZH做什么呢?为了尽早在龙王恢复所有力量之前将其杀死,他的任务是帮助大家计算最少需要多少时间才能把所有龙鳞屏障清除,你来帮帮他吧!

假设:

  • 单位时间为1s。所有混血种精英同时开始攻击

  • 每个混血种精英每次攻击时间为1s,只能攻击一片龙鳞,同时一片龙鳞只能被一个混血种精英攻击,保证精英数>屏障数

  • 所有混血种精英,包括路明非,每次攻击后可以随意交换攻击目标,不计交换时间

输入

输入包括多组数据;每组数据第一行为龙鳞屏障数n(1≤ n ≤105)及七宗罪单位时间伤害k(1≤ k ≤106);接下来一行为n个正整数x,分别代表每块龙鳞所能承受伤害量(1≤ x ≤109

输出

对于每组数据,输出一行,为击破屏障所需的最少时间(秒)。答案在int范围内。

#include <queue>
#include <cstdio>
#include<cmath>
using namespace std;
int n,k,X[100010],xmax;
int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        xmax = 0;
        for(int i = 0;i < n;i++)
        {
            scanf("%d",&X[i]);
            if(X[i] > xmax) xmax = X[i];
        }
        if(k == 1)
        {
            printf("%d\n",xmax);
            continue;
        }
        int ans = 0;
        int l = 1,r = xmax,mid;
        while(r >= l)
        {
            mid = (l + r)/2;
            long long cnt = 0;
            for(int i = 0;i < n;i++)
            {
                if(X[i] > mid)
                {
                    long long s = ceil(1.0 * (X[i] - mid)/(k - 1));
                    cnt += s;
                }
            }
            if(cnt > mid) l = mid + 1;
            else r = mid - 1,ans = mid;
        }
        printf("%d\n",ans);
    }
}

V AlvinZH的青春记忆III

二分图:最大二分匹配 | 最小顶点覆盖

简化题意

正魔相争,魔教弟子编号若是正道弟子编号的整数倍,此正道弟子将有杀身之祸,为了所有正道弟子此次平安历练,“焚香谷”掌门云易岚是杀戮之人,提出可将对应编号魔教弟子击杀,以保平安;而“天音寺”住持普泓大师主张仁义,提出可将对应编号正道弟子撤出此次历练,无需杀戮。道玄真人决定取中庸之道,一方面可以击杀部分魔教弟子,一方面撤出部分正道弟子,数量分别为 k1 和 k2,道玄真人想使 (k1+k2) 最小,请问这个最小值是多少呢?

输入

输入包括多组数据;每组数据第一行为两个正整数n、m,表示参与此次历练的正道弟子和魔教弟子数量(0<n,m≤1000);接下来一行为n个正整数,代表正道弟子的编号([1,106]),保证各不相同;再接下来一行是m个正整数,代表魔教弟子的编号([1,106]),保证各不相同。

输出

对于每组数据,输出一行,为描述中(k1+k2)的最小值。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
const int MAX_ = 1005;
const int INF = 0x3f3f3f3f;
using namespace std;
int n,m,ans;
vector<int> G[MAX_];
int match[MAX_],visit[MAX_];
int A[MAX_],B[MAX_];
bool dfs(int x)//增广路径
{
    for(int i = 0;i < G[x].size();i++)
    {
        int to = G[x][i];
        if(!visit[to])
        {
            visit[to] = true;
            if(!match[to] || dfs(match[to]))
            {
                match[to] = x;
                return true;
            }
        }
    }
    return false;
}
int MaxMatch()//求最大匹配
{
    ans = 0;
    memset(match,0,sizeof(match));
    for(int i = 1;i <= m;i++)
    {
        memset(visit,0,sizeof(visit));
        if(dfs(i)) ans++;
    }
    return ans;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i = 1;i < MAX_;i++) G[i].clear();//INIT
        for(int i = 1;i <= n;i++) scanf("%d",&A[i]);
        for(int i = 1;i <= m;i++)
        {
            scanf("%d",&B[i]);
            for(int j = 1;j <= n;j++)            
                if(B[i] %A[j] == 0) G[i].push_back(j);//加入路径            
        }
        printf("%d\n",MaxMatch());
    }
}

W AlvinZH的青春记忆IV

LCIS dp

天明并不想看所有的交战,他只想看势均力敌(评估战力相等)的对战,同时战力越高之间的对战一定会越精彩,天明想按战力从小到大的顺序“欣赏”对战。天明才不想给两个序列排序呢!他以当前的战力序列不变,按相对顺序从双方选出战力相同的进行对战,再从交战的两人之后再选战力更高且相等的二人对战。

即:若 Ai == Bj,则 Ai 可与 Bj 交战,之后下一场 Aii、Bjj 交战的条件是:Aii == Bjj、ii>i、jj>j、Aii>Ai、Bjj>Bj ;请问,调皮的天明最多可以看到多少局对战呢?

输入

输入包含多组数据;每组数据第一行为两个正整数n、m,代表天明名单上双方的人数(1 ≤ n,m ≤ 104)

接下来一行为n个正整数,代表墨家势力评估战力序列,[1,108]范围内

接下来一行为m个正整数,代表帝国势力评估战力序列,[1,108]范围内

输出

对于每组数据,输出一行,为天明可以看到的最大对战数。

输入样例

3 3
1 2 3
3 1 2
4 4
1 4 2 5
1 1 2 4

输出样例

2
2

样例解释

样例一:战力同为1一场,之后战力同为2一场。答案为2。

样例二:战力同为1一场,之后战力同为2或4一场。答案为2。

优化

注意:内存限制。long long是不可能的,int也是不可能的,AC是不可能的,除非你发现了nn的范围并不大,dp数组需要使用比int更小的short int

另外,本题最优解法时间复杂度为 O(n2) ,空间复杂度为O(n) 。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n, m, ans;
int A[10005];
int B[10005];
short int dp[2][10005];//注意类型
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i = 1;i <= n;i++) scanf("%d",&A[i]);
        for(int i = 1;i <= m;i++) scanf("%d",&B[i]);
        ans = 0;
        memset(dp, 0, sizeof(dp));
        int cur = 0;
        for(int i = 1; i <= n; i++)
        {
            cur = cur ^ 1;
            int maxx = 0;
            for(int j = 1; j <= m; j++)
            {
                if(A[i] == B[j]) dp[cur][j] = maxx + 1;
                else dp[cur][j] = dp[cur ^ 1][j];
                if(A[i] > B[j])
                    maxx = max(maxx, (int)dp[cur ^ 1][j]);
            }
        }
        for(int j = 1; j <= m; j++)
            if(ans < dp[cur][j]) ans = dp[cur][j];
        printf("%d\n", ans);
    }
}

X AlvinZH的1021实验

判断 前缀和应用

假设称量的器具只有一个天平和一些砝码,这些砝码有着特殊的重量,分别是1g、3g、9g、27g、81g、243g,每种砝码都只有一个;有一件神奇的事是:只用这些砝码,你可以称得任意不超过364g的整数克冰块,而且只有一种方法,你来想一想是如何做到的吧!

输入

输入包括多组数据;每组数据只有一个正整数n,代表要称量的冰块重量(1 ≤ n ≤ 364)。

输出

对于每组数据,输出一行字符串,表示称得此重量的方法,数字从高到低排列(具体见样例)。

输入样例

1
5
364

输出样例

1
9-3-1
243+81+27+9+3+1
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int weight[6] = {1,3,9,27,81,243};
int sum[6] = {1,4,13,40,121,364};
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int flag = 0;
        int i;//判断大小范围位置指标
        while(n > 0)
        {
            for(i = 0;i < 6;i++)
                if(n <= sum[i]) break;
            if(flag == 0)
            {
                n -= weight[i];
                printf("%d",weight[i]);
            }
            if(flag == 1)
            {
                n -= weight[i];
                printf("+%d",weight[i]);
            }
            if(flag == -1)
            {
                n = weight[i] - n;
                printf("-%d",weight[i]);
            }
            if(n > 0) flag = 1;
            else if(n < 0)
            {
                flag = -1;
                n = -n;
            }
        }
        printf("\n");
    }
}

Y AlvinZH的1021实验plus

覆盖思维 模拟

AlvinZH现在想称得从[1~m]之间的每个整数质量的物品,但他发现现有的砝码不够用,假设AlvinZH可以从实验箱里找到各种质量的砝码,请你帮他计算一下最少需要寻找多少个砝码。

输入

输入包括多组数据;每组数据第一行为初始砝码数n和想称得的最大质量m(0≤n≤1000,1≤m≤INT32_MAX)接下来一行包含n个数字,为现有的砝码的质量 mi (1 ≤ mi ≤ INT32_MAX)

输出

对于每组数据,输出一行,为需要寻找的最小砝码数

输入样例

2 6
1 3
3 5
1 2 2
3 20
1 5 10

输出样例

1
0
2

样例解释

样例一:初始只能称得[1]、[3]、[1+3],并不能称得[1~6]之间的所有质量,只需要寻找一个质量为2的砝码就可以了。([1]、[2]、[3]、[1+3]、[2+3]、[1+2+3])

样例二:初始可以称得[1]、[2]、[1+2]、[2+2]、[1+2+2],不需要寻找新的砝码

样例三:初始可以称得[1]、[5]、[1+5]、[1+10]、[5+10]、[1+5+10],需要寻找质量为2和4的砝码就ok

最小数目一定存在,但是方法可能不同

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
long long store[1005];
int main()
{
    int n,m;
    while (~scanf("%d%d",&n,&m))
    {
        memset(store,0, sizeof(store));
        for (int i = 0; i < n; ++i) scanf("%lld",&store[i]);
        long long miss = 1;//遮盖
        sort(store,store + n);
        long long pos = 0,ans = 0;
        while (miss <= m)
        {
            if(pos < n && store[pos] <= miss){miss += store[pos];pos++;}
            else{ans++;miss <<= 1;}
        }
        printf("%lld\n",ans);
    }
}

Z Bamboo and the Ancient Spell

LCS 含有适配符

题意简化

求两个字符串的LCS,其中'#'为全不可配符'?'为全配符

sample input

ABCDE
ZBD
AC#EB
C#BFG
ACE#?
KIKI#A
???
?#?

Sample output

2
2
1
2

两行输入一次输出

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
const int MAX_N = 105;
using namespace std;
string A,B;
int longestCommonSubsequence(string text1, string text2)
{
    int LCS[text1.size() + 1][text2.size() + 1];
    memset(LCS,0, sizeof(LCS));
    for (int i = 1; i <= text1.size(); ++i)
        for (int j = 1; j <= text2.size(); ++j)
        {
            if(text1[i - 1] != '#' 
               && text2[j - 1] != '#'
               && (text1[i - 1] == text2[j - 1] 
                   || text1[i - 1] == '?' 
                   || text2[j - 1] == '?'))
                LCS[i][j] = LCS[i - 1][j - 1] + 1;
            else
                LCS[i][j] = max(LCS[i - 1][j],LCS[i][j - 1]);
        }
    return LCS[text1.size()][text2.size()];
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    while(cin >> A >> B)    
        cout << longestCommonSubsequence(A,B) << endl;    
}

算法考试加油!!!

posted @ 2019-12-10 01:05  samshui  阅读(333)  评论(0编辑  收藏  举报