题意:有一本书有 n n n   卷,你需要从第一卷开始依次看,一旦没有某一卷就停止。在看之前 你可以进行若干次操作,每次卖掉任意两卷并买新的任意一卷。问操作结束后最多能看多少卷。
 
  做法 1 
注意到拥有的重复的卷都可以没有损失地卖掉,提前记录一下。然后从小到大扫,如果没有这一卷就尝试卖两本并买这一本。卖的两本优先从重复的卷里考虑,然后再贪心从大到小卖。正确性显然,数据结构维护即可。
By KrK 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include  <bits/stdc++.h>  using  namespace  std;int  n;set <int > S; int  cur;int  main ()  {    scanf ("%d" , &n);     for  (int  i = 0 ; i < n; i++) {         int  a; scanf ("%d" , &a);         if  (S.count (a)) cur++;         else  S.insert (a);     }     for  (int  i = 1 ; ; i++)         if  (!S.count (i)) {             while  (cur < 2  && !S.empty () && *S.rbegin () > i) {                 auto  it = S.end (); it--;                 cur++; S.erase (it);             }             if  (cur >= 2 ) {                 cur -= 2 ;                 S.insert (i);             } else  {                 printf ("%d\n" , i - 1 );                 return  0 ;             }         }     return  0 ; } 
 
  做法 2 
考虑二分答案。对于读 i i i   卷的情况,将 > i + 1 >i+1 > i + 1   的卷和 ≤ i \le i ≤ i   的重复的卷全部卖掉替换,判断是否能填补空隙。
By heno239 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void  solve ()   {    int  n; cin >> n;     vector<int > a (n)  ;     rep (i, n)cin >> a[i];          auto  can = [&](int  x) {         vector<int > cnt (x+1 );         int  z = 0 ;         rep (i, n) {             if  (a[i] > x)z++;             else  {                 if  (cnt[a[i]])z++;                 cnt[a[i]] = 1 ;             }         }         int  r = 0 ;         rep1 (i, x) {             if  (!cnt[i])r++;         }         if  (z >= 2  * r)return  true ;         return  false ;     };     int  le = 0 , ri = n + 5 ;     while  (ri - le > 1 ) {         int  m = (le + ri) / 2 ;         if  (can (m)) {             le = m;         }         else  {             ri = m;         }     }     cout << le << "\n" ; } 
 
  做法 3 
与二分答案类似,但将二分改成了枚举。注意到读的卷数一定不会超过 n n n  ,所以 > n >n > n   的卷都可以直接卖掉。考虑统计能否读 i i i   卷。考虑设计 b a k [ i ] bak[i] b a k [ i ]   表示 ≤ i \le i ≤ i   的卷有多少种 ,则如果要读 i i i   卷,将会有 i − b a k [ i ] i-bak[i] i − b a k [ i ]   个位置空缺。这些位置可以使用多余的补上,而只有 b a k [ i ] bak[i] b a k [ i ]   卷是不多余的(每种只有一卷不多余,剩下的都多余),所以总的多余的数量为 n − b a k [ i ] n-bak[i] n − b a k [ i ]  ,比较空缺位置与多余数量的一半即可。
By He_Ren 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <bits/stdc++.h>  using  namespace  std;typedef  long  long  ll;typedef  pair<int ,int > pii;const  int  MAXN = 3e5  + 5 ;int  a[MAXN], bak[MAXN];int  main (void )  {    int  n;     scanf ("%d" ,&n);     for (int  i=1 ; i<=n; ++i)         scanf ("%d" ,&a[i]);     for (int  i=1 ; i<=n; ++i)         if (a[i] <= n) bak[a[i]] = 1 ;     for (int  i=1 ; i<=n+1 ; ++i)         bak[i] += bak[i-1 ];     for (int  i=1 ; i<=n+1 ; ++i)     {         if (i - bak[i] > (n - bak[i]) / 2 )         {             printf ("%d\n" ,i-1 );             return  0 ;         }     }     return  0 ; } 
 
  错解 
By daisybunny 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <bits/stdc++.h>  using  namespace  std;int  n;int  a[300005 ];map<int ,int > mp; int  ba;int  ans;int  main ()  {    cin>>n;     for (int  t=0 ;t<n;t++)     {         cin>>a[t];         mp[a[t]]++;     }     sort (a,a+n);     for (int  t=1 ;;t++)     {         if (mp[t]==0 )         {             if (ba>1 )             {                 ba-=2 ;                 ans++;             }             else  if (n>=1 &&t<a[n-1 ]&&ba==1 )             {                 mp[a[n-1 ]]--;n--;ba--;ans++;             }             else  if (n>=2 &&t<a[n-2 ])             {                 mp[a[n-1 ]]--;mp[a[n-2 ]]--;n-=2 ;ans++;             }             else                  break ;         }         else          {             ans++;             ba+=mp[t]-1 ;             mp[t]--;         }     }     cout<<ans;     return  0 ; } 
 
一组 hack 数据为
 
此时在算到 2 的空缺时,该代码会优先卖掉两个 5,而最优解应该卖掉多余的一个 4 一个 5。此程序的问题在于,他考虑到了优先卖掉重复的,但只判断了优先卖掉 ≤ i \le i ≤ i   的重复的,而没有优先处理 > i >i > i   的重复。
题意:有 n n n   张卡片,第 i i i   张正面为 a i a_i a i   ,反面为 b i b_i b i   ,问是否存在一种翻转方案使得卡片的和为 S S S  。如果有,输出方案。
 
设 d p [ i ] [ j ] = 0 / 1 dp[i][j]=0/1 d p [ i ] [ j ] = 0 / 1   表示前 i i i   张卡片能否得到 j j j  ,则
d p [ i ] [ j ] = d p [ i − 1 ] [ j − a i ]  or  d p [ i − 1 ] [ j − b i ] dp[i][j]=dp[i-1][j-a_i] \text{ or } dp[i-1][j-b_i]
 d p [ i ] [ j ] = d p [ i − 1 ] [ j − a i  ]  or  d p [ i − 1 ] [ j − b i  ] 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <bits/stdc++.h>  using  namespace  std;int  a[110 ],b[110 ];bool  f[110 ][10010 ],pre[110 ][10010 ];void  get (int  now,int  x)   {    if (now==0 ) return ;     if (pre[now][x])         get (now-1 ,x-a[now]);     else  get (now-1 ,x-b[now]);     cout<<(pre[now][x]?'H' :'T' ); } signed  main ()   {    int  n,s;     cin>>n>>s;     for (int  i=1 ;i<=n;i++)         cin>>a[i]>>b[i];     f[0 ][0 ]=1 ;     for (int  i=1 ;i<=n;i++)         for (int  j=1 ;j<=s;j++) {             if (j>=a[i]&&f[i-1 ][j-a[i]])                 f[i][j]=1 ,pre[i][j]=1 ;             if (j>=b[i]&&f[i-1 ][j-b[i]])                 f[i][j]=1 ,pre[i][j]=0 ;         }     if (f[n][s]) {         cout<<"Yes" <<endl;         get (n,s);         cout<<endl;     }     else  cout<<"No" <<endl;     return  0 ; } 
 
题意:有 n n n   个点 m m m   条有向有权边 u i → w i v i u_i\xrightarrow{w_i} v_i u i  w i   v i    和一个边的编号序列 e i e_i e i   ,你可以选择编号序列的一个子序列,使得该子序列的边构成一条 1 → n 1\to n 1 → n   的路径,求最短路径。
 
直接 DP。令 f i , j f_{i,j} f i , j    表示只考虑序列中前 j j j   条边,1 → j 1\to j 1 → j   的最短路径,则
f i , j = { min  ( f i − 1 , j , f i − 1 , u e i + w e i )  if  v e i = j f i − 1 , j  otherwise f_{i,j}=\begin{cases}\min(f_{i-1,j},f_{i-1,u_{e_i}}+w_{e_i})\text{ if }v_{e_i}=j\\f_{i-1,j}\text{ otherwise}\end{cases}
 f i , j  = { min ( f i − 1 , j  , f i − 1 , u e i    + w e i   )  if  v e i   = j f i − 1 , j   otherwise  
此时第一维可以省略。转移时只需要更新 f v a i f_{v_{a_i}} f v a i      即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <bits/stdc++.h>  using  namespace  std;#define  int long long struct  edge  {    int  u,v,w; } a[200010 ]; int  e[200010 ];int  f[200010 ];signed  main ()   {    int  n,m,k;     cin>>n>>m>>k;     for (int  i=1 ;i<=m;i++)         cin>>a[i].u>>a[i].v>>a[i].w;     for (int  i=1 ;i<=k;i++)         cin>>e[i];     memset (f,-1 ,sizeof  (f));     f[1 ]=0 ;     for (int  i=1 ;i<=k;i++) {         if (f[a[e[i]].u]==-1 ) continue ;         if (f[a[e[i]].v]==-1 ||f[a[e[i]].v]>f[a[e[i]].u]+a[e[i]].w)             f[a[e[i]].v]=f[a[e[i]].u]+a[e[i]].w;     }     cout<<f[n]<<endl;     return  0 ; } 
 
题意:有一个 n × n n\times n n × n   的矩阵,每个格子有一个正整数,每次只能向右走或向下走,问有多少条路径能从 ( 1 , 1 ) (1,1) ( 1 , 1 )   走到 ( n , n ) (n,n) ( n , n )  ,且路径异或和为 0 0 0  。
 
双向搜索。搜索从 ( 1 , 1 ) (1,1) ( 1 , 1 )   到副对角线的所有方案,存到 m a p map m a p   里,再搜索从 ( n , n ) (n,n) ( n , n )   到副对角线的所有方案,计算答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <bits/stdc++.h>  using  namespace  std;int  n,a[30 ][30 ];map<int ,int > m[2 ][21 ]; void  dfs (int  x,int  y,int  f,int  flag)   {    f^=a[x][y];     if (x+y==n+1 ) m[flag][x][f]++;     else  if (flag) dfs (x-1 ,y,f,flag),dfs (x,y-1 ,f,flag);     else  dfs (x+1 ,y,f,flag),dfs (x,y+1 ,f,flag); } signed  main ()   {    cin>>n;     for (int  i=1 ;i<=n;i++)         for (int  j=1 ;j<=n;j++)             cin>>a[i][j];     dfs (1 ,1 ,0 ,0 );     dfs (n,n,0 ,1 );     long  long  ans=0 ;     for (int  i=1 ;i<=n;i++) {         int  x=a[i][n+1 -i];         for (auto  tmp:m[0 ][i])             ans+=1ll *tmp.second*m[1 ][i][tmp.first^x];     }     cout<<ans<<endl;     return  0 ; }