題目
收集卡片
Description
Star 計劃訂購一本將要發行的週刊雜誌,但他可不是爲了讀書,而是——集卡。已知雜誌將要發行 N 周(也就是 N 期),每期都會附贈一張卡片。Star通過種種途徑,瞭解到 N 期雜誌附贈的卡片種類。Star 只想訂購連續的若干期,並在這些期內收集所有可能出現的種類的卡片。現在他想知道,他最少需要訂購多少期。
Input
第一行一個整數 N;
第二行一個長度爲 N 的字符串,由大寫或小寫字母組成,第 i 個字符表示 第 i 期附贈的卡片種類,每種字符(區分大小寫)表示一種卡片。
Output
輸出一行一個整數,表示 Star 最少訂購的期數。
Sample Input
8
acbbbcca
Sample Output
3
Data Constraint
對於 30%的數據,N ≤ 300;
對於 40%的數據,N ≤ 2000;
對於 60%的數據,N ≤ 5000;
對於 80%的數據,N ≤ 100000;
對於 100%的數據,N ≤ 500000。
基因變異
Description
21 世紀是生物學的世紀,以遺傳與進化爲代表的現代生物理論越來越多的 進入了我們的視野。 如同大家所熟知的,基因是遺傳因子,它記錄了生命的基本構造和性能。 因此生物進化與基因的變異息息相關,考察基因變異的途徑對研究生物學有着 至關重要的作用。現在,讓我們來看這樣一個模型:
1、所有的基因都可以看作一個整數或該整數對應的二進制碼;
2、在 1 單位時間內,基因 x 可能會在其某一個二進制位上發生反轉;
3、在 1 單位時間內,基因 x 可能會遭到可感染基因庫內任一基因 y 的影響 而突變爲 x XOR y。
現在給出可感染基因庫,Q 組詢問,每組給出初始基因與終止基因,請你 分別計算出每種變異最少要花費多少個單位時間。
Input
第 1 行兩個整數 N, Q; 第 2 行 N 個用空格隔開的整數分別表示可感染基因庫內的基因; 接下來 Q 行每行兩個整數 S、T,分別表示初始基因與終止基因。
Output
輸出 Q 行,依次表示每組初始基因到終止基因間最少所花時間。
Sample Input
3 3
1 2 3
3 4
1 2
3 9
Sample Output
2
1
2
Data Constraint
對於 20%的數據,N = 0;
對於額外 40%的數據,1 ≤ Q ≤ 100,所有基因表示爲不超過 的非負整 數;
對於 100%的數據,0 ≤ N ≤ 20,1 ≤ Q ≤ ,所有基因表示爲不超過 的 非負整數。
abcd
Description
有4個長度爲N的數組a,b,c,d。現在需要你選擇N個數構成數組e,數組e滿足以及並且使得最大。
Input
輸入文件名爲abcd. in。
輸入文件共 N+1 行。
第 1 行包含1個正整數N。
第 i+1 行包含4個整數a[i],b[i],c[i],d[i]。
Output
輸出文件名爲abcd.out。
輸出共1行,包含1個整數,表示所給出公式的最大值。輸入數據保證一定有解。
Sample Input
Sample1:
5
-1 1 2 5
-2 2 1 2
0 1 1 3
-2 -1 3 10
-2 2 3 9
Sample2:
10
1 10 1 7
-10 10 2 0
-10 10 2 2
-10 10 2 0
1 10 1 0
-10 10 2 0
10 10 2 0
1 10 1 0
-10 10 2 0
1 10 1 0
Sample3:
10
1 10 1 0
-10 10 2 2
-10 10 2 2
-10 10 2 2
1 10 1 0
-10 10 2 2
-10 10 2 2
1 10 1 0
-10 10 2 2
1 10 1 0
Sample Output
Sample1:
2
Sample2:
90
Sample3:
-4
Data Constraint
對於20%的數據,N≤10,-2≤a[i]<b[i]≤2;
對於60%的數據,N≤50, -20≤a[i]<b[i]≤20;
對於100%的數據,N≤200,-25≤a[i]<b[i]≤25,1≤c[i]≤20,0≤d[i] ≤100000。
總結
今天犯了一個錯誤,坑了100分,只有100分了。
第一題:
我先想到了二分答案、前綴和等高深的算法。好不容易纔回過神來,發現這是一道很水很水的雙向指針。在想完第二題後打代碼,考場AC。
第二題:
一開始覺得很神仙,就去看第三題,結果發現第三題更神仙o(╥﹏╥)o
於是決定模擬一下樣例。由於我理解錯了題意,把
在 1 單位時間內,基因 x 可能會在其某一個二進制位上發生反轉;
理解成了可以在1單位時間內翻轉基因 x 的所有二進制位,因此怎麼算也算不出來;等我算完樣例後,驚奇地發現了一個神奇的東西:
把基因 x 變成基因 y 等價於把 0 變成 x^y(即 x 和 y 有多少個不同的位)
於是我欣欣然打了一個DP:設表示把基因 i 變成0的最少步數。其中表示xor。
結果發現這個DP有後效性,遂改成記憶化搜索(DFS),結果時間複雜度似乎不太理想。
正常人這個時候都會把DFS改成BFS,然而我卻腦殘了一般,輸出 f 數組,然後在代碼的開頭直接給 f 數組賦初值。
其實這樣是會出錯的,因爲每一個輸入的 a 都不一樣,那麼 f 的值就自然不同了;然而我卻沒有意識到這一點,直接交到OJ上。
注意碼量!
是不是很驚人?我TMD給那麼大的DP數組賦初值,結果直接編譯錯誤了(OJ能容忍的最長碼量似乎是byte)
不過話說回來,這樣的實驗似乎是挺有趣的。
正解
T1
雙指針搜索就可以了。
下面就來解釋什麼是雙指針搜索。
首先申明一下,這裏的指針並非指數據結構中的那個指針,而是兩個指向數組中位置的變量。我們要兩個指針 l 和 r,分別指向子區間的開頭和結尾。
這個算法常用來解決在大區間中找滿足要求的最長或最短的子區間。它的步驟常是這樣:
若當前區間不滿足要求(有時也可以是滿足要求),且若是該區間向右擴張後可能滿足,即向右擴張可接近要求,則把 r 指針向右移動1個單位,並加上該元素的值;
若當前區間已經超出要求(有時是滿足要求),且該區間向右擴張會不斷遠離要求,則把 l 指針向右移動1個單位。
注意:在所有這些過程中,l 和 r指針永遠向右移動
在這題中,我們先把兩個指針指針指向第一個元素,再執行上面的過程。如果當前區間不包含所有字符,則 r 向右移動;包含所有字符,就把 l 向右移動(因爲我們這道題目是要求最短序列的)。
最後,請注意邊界條件!
T2
這題其實也不難(然而我卻爆0了)
首先,我們可以發現一個顯而易見的規律(不要問我爲什麼想了20分鐘才發現)
把基因 x 變成基因 y 等價於把 0 變成 x^y(即 x 和 y 有多少個不同的位)
因此,我們可以先預處理出所有數變成0的最少步數(很容易發現把x變成y等價於把y變成x嘛)
可以從0出發,搜索所有它可以變成的數,若可以更新,就更新它並從它出發進行搜索。
顯然DFS會TLE,我們就要加上記憶化;然而記憶化DFS還是會爆炸,於是我就想到了打表(不要問我爲什麼想不到BFS!!!)
其實用BFS就可以了。。。
T3
這題不少人用水法過了。
其實我們可以把它轉化成一道多重揹包問題:
給出n個物品,每一件可以選到個,體積爲,價值爲,恰好填滿一個容量爲0的揹包
對於這個問題,我們可以把範圍轉換成0~b[i]-a[i],那麼選的件數就成了e[i]-a[i],那麼揹包的體積就成了-a[i]*c[i](因爲a[i]幾乎總是小於0),價值是。
但是這樣DP顯然時超,因此我們要用優化。有兩種:單調隊列和二進制。
這裏說一下二進制優化:
設物品的體積爲x,我們就把它分解成體積爲1,2,4,8,…,的物品。
可以證明,這些物品可以組成0~x中的任意體積。
這樣做就可以了。
代碼
T1
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 500005
struct node
{char ch;int id;}s[N];
int a[N],num[N];
inline bool cmp(node x,node y)
{return x.ch<y.ch;}
int main()
{
int n,m=1,i,j,l,r,ans=N;
bool bk;
scanf("%d\n",&n);
for(i=1;i<=n;i++) scanf("%c",&s[i].ch),s[i].id=i;
sort(s+1,s+n+1,cmp);
a[s[1].id]=1;
for(i=2;i<=n;i++)
{
if(s[i].ch>s[i-1].ch) m++;
a[s[i].id]=m;
}
for(l=r=1,num[a[1]]=1;l<=n;--num[a[l++]])
{
bk=1;
for(;bk&&r<=n;num[a[++r]]++)
{
for(j=1;j<=m;j++)
if(!num[j])
break;
if(j>m){bk=0;break;}
}
if(bk) break;
if(r-l+1<ans) ans=r-l+1;
}
printf("%d\n",ans);
return 0;
}
T2
#include<cstdio>
using namespace std;
#define M 1048576
#define N 25
int n,a[N],f[M],data[5110000];
int main()
{
int m,i,j,x,y,head=0,tail=1;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
for(i=1;i<M;i++) f[i]=N;
while(head<tail)
{
x=data[++head];
for(i=1;i<=n;i++)
{
if(f[x^a[i]]>f[x]+1)
{
f[x^a[i]]=f[x]+1;
data[++tail]=x^a[i];
}
}
for(i=0;i<20;i++)
{
if(f[x^1<<i]>f[x]+1)
{
f[x^1<<i]=f[x]+1;
data[++tail]=x^1<<i;
}
}
}
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y),x^=y;
printf("%d\n",f[x]);
}
return 0;
}
T3
#include<cstdio>
using namespace std;
#define N 205
#define M 100005
#define inf 999999999
int n,m,ans,s,f[M],a[N],b[N],c[N],d[N],w[N*6],v[N*6];
int main()
{
freopen("abcd.in","r",stdin);
freopen("abcd.out","w",stdout);
int i,j,k;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
m-=a[i]*c[i],ans+=a[i]*d[i];
}
for(i=1;i<=n;i++)
{
k=b[i]-a[i];
for(j=1;j<=k;j<<=1)
{
w[++s]=j*c[i],v[s]=j*d[i];
k-=j;
}
if(k) w[++s]=k*c[i],v[s]=k*d[i];
}
for(i=1;i<M;i++) f[i]=-inf;
for(i=1;i<=s;i++)
{
for(j=m;j>=w[i];j--)
{
if(f[j]<f[j-w[i]]+v[i])
f[j]=f[j-w[i]]+v[i];
}
}
printf("%d\n",ans+f[m]);
return 0;
}