0 粉刷匠
windy有 N 條木板需要被粉刷。
每條木板被分爲 M 個格子。
每個格子要被刷成紅色或藍色。
windy每次粉刷,只能選擇一條木板上一段連續的格子,然後塗上一種顏色。
每個格子最多隻能被粉刷一次。
如果windy只能粉刷 T 次,他最多能正確粉刷多少格子?
一個格子如果未被粉刷或者被粉刷錯顏色,就算錯誤粉刷。
100%的數據,滿足 1 <= N,M <= 50 ; 0 <= T <= 2500 。
————————————————————————————————————
每個格子最多隻能被粉刷一次
很好沒有後效性了,秒變水
相當於分組揹包,先在組內DP,再做揹包
對於每條木板:設表示第i個格子粉刷了j次的最大正確粉刷數;
對於所有木板:設
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n,m,t,s[60];
int h[60][60],f[2600][60];
int main(){
scanf("%d%d%d",&n,&m,&t);
for (int i=1;i<=n;i++){
char ch[60];
scanf("%s",ch+1);
memset(h,0,sizeof h);
for (int j=1;j<=m;j++){
s[j]=s[j-1]+ch[j]-'0';
for (int l=1;l<=min(j,t);l++)
for (int k=0;k<j;k++)
h[j][l]=max(h[j][l],max(h[k][l],h[k][l-1]+max(s[j]-s[k],j-k-s[j]+s[k])));
}
for (int j=0;j<=min(t,i*m);j++)
for (int k=0;k<=min(j,m);k++)
f[j][i]=max(f[j-k][i-1]+h[m][k],f[j][i]);
}
printf("%d",f[t][n]);
}
1 迷路
windy在有向圖中迷路了。
該有向圖有 N 個節點,windy從節點 0 出發,他必須恰好在 T 時刻到達節點 N-1。
現在給出該有向圖,你能告訴windy總共有多少種不同的路徑嗎?
注意:windy不能在某個節點逗留,且通過某有向邊的時間嚴格爲給定的時間。
100%的數據,滿足 2 <= N <= 10 ; 1 <= T <= 1000000000 。
你說什麼?矩陣乘法?我學過,我不會[菜雞呆滯.jpg]
什麼?矩陣乘法快速冪?什麼東西?[鹹魚呆滯.jpg]
矩陣乘法定義:a*b=c———》
其中a矩陣與b矩陣必有一邊等長
矩陣乘法快速冪:將快速冪中的的數a換成矩陣a,其中a的長寬需要相等,
大部分矩陣乘法的題目都會用到快速冪
矩陣乘法可用於優化動態規劃,這題用到的是矩陣乘法在鄰接矩陣中的一個定義:
定義該鄰接矩陣爲一個圖,a[i][j]表示i到j有邊,表示i在走b次後恰好到達j的方法數
哇好東西吶[無知的鄉下蒟蒻呆滯.jpg]
在這道題中,邊權較小,可以強行拆點然後矩陣乘法快速冪,
拆點具體做法:
將編號爲i點拆爲編號爲i * 9~i * 9+8的9個點
對於一條非0的邊( i , j )= k , ,並將點的路徑加入矩陣,相當於從點i走到點j經過了k條邊權爲1的點,總邊權等價於原邊權k
#include <cstdio>
#include <cstring>
using namespace std;
int n,t;
int a[125][125],b[125][125];
void read(){
scanf("%d%d",&n,&t);
for (int i=0;i<n;i++){
char ch[15];
scanf("%s",ch);
for (int j=0;j<n;j++)
if (ch[j]-'0'>0)
a[i*9+ch[j]-'0'-1][j*9]=1;
for (int j=0;j<8;j++)
a[i*9+j][i*9+j+1]=1;
}
}
void ksm(int t){
int c[125][125];
for (int i=0;i<=n*9;i++)
for (int j=0;j<=n*9;j++)
b[i][j]=a[i][j];
while (t){
if (t&1==1) {
memset(c,0,sizeof c);
for (int i=0;i<=n*9;i++)
for (int j=0;j<=n*9;j++)
for (int k=0;k<=n*9;k++)
c[i][j]=(c[i][j]+a[i][k]*b[k][j])%2009;
for (int i=0;i<=9*n;i++)
for (int j=0;j<=9*n;j++)
b[i][j]=c[i][j];
}
memset(c,0,sizeof c);
for (int i=0;i<=n*9;i++)
for (int j=0;j<=n*9;j++)
for (int k=0;k<=n*9;k++)
c[i][j]=(c[i][j]+a[i][k]*a[k][j])%2009;
for (int i=0;i<=9*n;i++)
for (int j=0;j<=9*n;j++)
a[i][j]=c[i][j];
t=t>>1;
}
}
int main(){
read();
ksm(t-1);
printf("%d",b[0][(n-1)*9]) ;
}
2 遊戲
windy學會了一種遊戲。 對於1到N這N個數字,都有唯一且不同的1到N的數字與之對應。 最開始windy把數字按順序1,2,3,……,N寫一排在紙上。 然後再在這一排下面寫上它們對應的數字。 然後又在新的一排下面寫上它們對應的數字。 如此反覆,直到序列再次變爲1,2,3,……,N。 如: 1 2 3 4 5 6 對應的關係爲 1->2 2->3 3->1 4->5 5->4 6->6 windy的操作如下
1 2 3 4 5 6
2 3 1 5 4 6
3 1 2 4 5 6
12 3 5 4 6
2 3 1 4 5 6
3 1 2 5 4 6
1 2 3 4 5 6
這時,我們就有若干排1到N的排列,上例中有7排。 現在windy想知道,對於所有可能的對應關係,有多少種可能的排數。
100%的數據,滿足 1 <= N <= 1000 。
沒太明白題意?感覺題目描述和輸出描述矛盾?
不管,事實上是按照題目描述來的,求可能的排數的種數
題意中有兩種數量,排數和排數的種數
隨便舉一個大一點的n(比如10),再隨便列一種對應關係,手動模擬過程,發現對應關係必定構成循環對應
比如1->2, 2->5, 5->1 就是一個大小爲3的循環
在這樣的一個循環中,循環它的大小n次後將回到正確對應(即每個數的位置是自己)
而一個n可能有多個不同的循環,排數就是這些循環大小的lcm(最小公倍數)
在同一種對應關係中,循環大小的和不超過n,而又因爲可以用1來填充,所以可以不到n
排數lcm質因數分解後=
又有
就轉化爲一個類似揹包的東西,先篩質數,然後對每個質數的k次方()做揹包,每種質數只能選一次
#include <cstdio>
#include <cstring>
using namespace std;
int n;
long long ans;
long long f[1050];
int b[1050],c[1050],cnt;
int main(){
scanf("%d",&n);
for (int i=2;i<=n;i++){
if (b[i]==0){
b[i]=1;
c[++cnt]=i;
}
for (int j=1;i*c[j]<=n&&j<=cnt;j++){
b[i*c[j]]=1;
if (i%c[j]==0) break;
}
}
f[0]=1;
for (int i=1;i<=cnt;i++)
for (int j=n;j>=c[i];j--)
for (int k=c[i];k<=j;k*=c[i])
f[j]+=f[j-k];
for (int i=0;i<=n;i++)
ans=(long long)ans+f[i];
printf("%lld",ans);
}
3 windy數
windy定義了一種windy數。
不含前導零且相鄰兩個數字之差至少爲2的正整數被稱爲windy數。
windy想知道,在A和B之間,包括A和B,總共有多少個windy數?
100%的數據,滿足 1 <= A <= B <= 2000000000 。
這種l和r之間有多少個xxx的一般轉化爲1–n有多少的xxx,ans=ans[r]-ans[l]
然後這個滿足某某條件的xx數就用數位DP求
設表示第i個數字,最後一個數字爲j有多少個xx數
統計答案時,先把位數小於n的加上,再把位數等於n但是首數字小於n的加上
然後枚舉第一個數字、第二個數字~最後一個數字等於n的加上
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int a,b;
int f[20][20];
int read(){
int x=0;
char ch;
ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x;
}
void dp(){
for (int i=0;i<=9;i++) f[1][i]=1;
for (int i=2;i<=10;i++)
for (int j=0;j<=9;j++)
for (int k=0;k<=9;k++)
if (abs(j-k)>=2)
f[i][j]+=f[i-1][k];
}
int work(int x){
int s[20],ans=0;
memset(s,0,sizeof s);
while (x){
s[++s[0]]=x%10;
x/=10;
}
for (int i=1;i<s[0];i++)
for (int j=1;j<=9;j++)
ans+=f[i][j];
for (int i=1;i<s[s[0]];i++)
ans+=f[s[0]][i];
for (int i=s[0]-1;i>=1;i--){
for (int j=0;j<s[i];j++){
if (abs(j-s[i+1])>=2)
ans+=f[i][j];
}
if (abs(s[i+1]-s[i])<2) break;
}
return ans;
}
int main(){
a=read(),b=read();
dp();
printf("%d",work(b+1)-work(a));
}