題解 P4310 【絕世好題】

先無良宣傳一下博客 $wwwwww$
文章列表 - 核融合爐心 - 洛谷博客git


知識點:DP , 奇妙思路 , 暴力枚舉(?)

一道絕世好題函數

  • 題目要求:

    求 $a$ 的子序列 $b$ 的最長長度,
    知足 $bi\ &\ bi-1\not=0$
    $n \le 1e5 , a_i \le 1e9$優化

  • 暴力思路:

    類比最長上升子序列spa

    直接 $O(n^2)$ 暴力枚舉
    當前要添加的數,以及序列 $b$ 結尾的數code

    設 $f[i]$ 表示以$a[i]$結尾的 $b$序列的最長長度
    則: 狀態轉移方程式爲:
    $\large f[i] = max(f[i], f[j]+1)\ (a[i]\ &\ a[j] \not=0)$blog

    能夠取得 $90$ 分的好成績(大霧)get

  • 考慮優化

    發現新添加的數,
    只能由:
    在同一二進制位上 , 同爲1的數轉移而來博客

    也就是說,
    能夠選擇 枚舉
    新添加的數的 二進制上的1位it

    考慮枚舉二進制位
    並記錄 :
    此二進制位全爲爲$1$的數 組成的 子序列$b$
    所能達到的最大長度爲多少io

    設 $f[i]$ 表示 : 最後一位爲 $i$ 的 $b$ 數列的最長長度,
    $bit[j]$ 表示 : 二進制第 $j$ 位爲 $1$ 的數 , 組成的子序列 $b$ 的最長長度
    $k$ 爲 枚舉的: $a[i]$ 中 , 二進制上爲 $1$ 的二進制位數

    則能夠推出新的狀態轉移方程式:
    $ \large f[i] = max(f[i] , bit[k]+1)$

    這樣 就能夠少一層循環
    來枚舉 新添加的數 可接到 哪些數以後.

    更新完 $f[i]$ 後 , 再用更新後的 $f[i]$ ,
    反過來 更新 $bit[k]$

    對於枚舉 $k$ , 可使用 $lowbit()$ , 並取其 $log$ 函數值來得到
    在更新 $f[i]$ 的過程當中取最大的 $f[i]$ 做爲答案
    最後優化到了 $O(31 \times n)$ .
    (由於最多隻有 $31$ 個二進制位上的 $1$ )


上代碼:

$O(n^2)$ 暴力90分:

#include<cstdio>
#include<ctype.h>
#include<algorithm>
const int MARX	= 1e5+10;;
//=============================================================
int n,ans,a[MARX];
int f[MARX];
//=============================================================
inline int read()
{
    int s=1, w=0; char ch=getchar();
    for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
    for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
    return s*w;
}
//=============================================================
signed main()
{
	n=read();
	for(int i=1;i<=n;i++) a[i]=read(),f[i]=1;//讀入並初始化
	
	for(int i=2;i<=n;i++)
	  for(int j=1;j<i;j++)
	    if(a[i] & a[j])
		  f[i]=std::max(f[i],f[j]+1),//更新f[i]並找到最大值 
		  ans=std::max(ans,f[i]);
	printf("%d",ans);
}

$O(31\times n)$ $100$分

#include<cstdio>
#include<algorithm>
#include<map>
#include<ctype.h>
#define lowbit(x) (x)&-(x)
const int MARX = 1e5+10;
//=============================================================
int n,ans,a[MARX]; 
int bit[40] , f[MARX];  //具體意義見上文
std::map <int,int> log_2;
//=============================================================
inline int read()
{
	int fl=1,w=0;char ch=getchar();
	while(!isdigit(ch) && ch!='-') ch=getchar();
	if(ch=='-') fl=-1;
	while(isdigit(ch)){w=w*10+ch-'0',ch=getchar();}
	return fl*w;
}
//=============================================================
signed main()
{
	for(int i=0,sum=1;i<=31;i++,sum<<=1) log_2[sum]=i; //預處理log函數 
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++)
	{
	  for(int j=a[i],low=lowbit(j);j;j-=low,low=lowbit(j)) //枚舉二進制位更新f[i] 
	    f[i]=std::max(f[i],bit[log_2[low]]+1);
	  for(int j=a[i],low=lowbit(j);j;j-=low,low=lowbit(j)) //使用更新過的f[i]更新bit[k] 
	    bit[log_2[low]]=std::max(bit[log_2[low]],f[i]);	
	  ans=std::max(f[i],ans); //取得最大答案 
	}
	printf("%d",ans);
}

$updata\ on\ 2019.8.13$ 修復了暴力思路的 $bug$ , 並添加了代碼

相關文章
相關標籤/搜索