1. 程式人生 > >20181025小結-2

20181025小結-2

Big Barn 巨大的牛棚
棋盤製作
牛線Cow Line
傳紙條
OKR-Periods of Words

【以上均出自WOJ】



Big Barn 巨大的牛棚

矩陣DP
動態規劃
f [ i ] [ j ] = min ( min ( f [ i ] [ j-1] , f [ i - 1 ] [ j ] ) , f [ i - 1 ] [ j - 1 ] ) + 1 ;
f(i, j)表示以(i, j)為右下角的最大正方形的邊長。
此位置上沒種樹時才更新。
通俗易懂

#include<bits/stdc++.h>
using namespace std;
int n,m,ans=0;
int a[1005][1005],f[1005][1005];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		a[x][y]=1;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(!a[i][j]){//沒樹 
				f[i][j]=min(min(f[i-1][j-1],f[i-1][j]),f[i][j-1])+1;
				if(f[i][j]>ans)ans=f[i][j];
			}
	printf("%d",ans);
	return 0;
}

棋盤製作

矩陣DP
動態規劃
懸線法
qwq ^ __ ^ qwq的演算法二:懸線法+上面的最大正方形
* _ *加深印象
先預處理染色一下很重要哦!!!

#include<bits/stdc++.h>
using namespace std;
int n,m,ans1=0,ans2=0,tmp;
int a[2001][2001],f[2001][2001];//
void work1(){//正方形 
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(a[i][j]){//見WOJ1787 
				f[i][j]=min(min(f[i-1][j-1],f[i-1][j]),f[i][j-1])+1;
				if(f[i][j]>ans1)ans1=f[i][j];
			}
}
int l[2001][2001],r[2001][2001];//整個上方矩形左右最近障礙  
int dl[2001][2001],dr[2001][2001];//當前行左右最近障礙 ()
int h[2001][2001];//高度 
void work2(){
	for(int i=1;i<=n;i++){
		tmp=0;
		for(int j=1;j<=m;j++)//計算左邊最近障礙位置
			if(a[i][j])dl[i][j]=tmp;
			else{
				tmp=j;
				l[i][j]=0;//細節 
			}
		tmp=m+1;
		for(int j=m;j>0;j--)//計算左邊最近障礙位置
			if(a[i][j])dr[i][j]=tmp;
			else{
				tmp=j;
				r[i][j]=m+1;//細節 
			}
	}
	for(int i=1;i<=m;i++)r[0][i]=m+1;//細節
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(a[i][j]){
				h[i][j]=h[i-1][j]+1;
				l[i][j]=max(l[i-1][j],dl[i][j]);//計算當前矩形最左遇到的障礙
                r[i][j]=min(r[i-1][j],dr[i][j]);//計算當前矩形最右遇到的障礙
                ans2=max(ans2,h[i][j]*(r[i][j]-l[i][j]-1));
			}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			scanf("%d",&a[i][j]);
			if((i+j)%2)a[i][j]^=1;//在相應位置染上相反色
		}
	work1();
	work2();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			a[i][j]^=1;//重新染色 
	memset(f,0,sizeof(f));
	memset(l,0,sizeof(l));
	memset(r,0,sizeof(r));
	memset(dl,0,sizeof(dl));
	memset(dr,0,sizeof(dr));
	memset(h,0,sizeof(h));
	work1();
	work2();
	printf("%d\n%d",ans1*ans1,ans2);//最大正方形   最大矩形 
	return 0;
}

牛線Cow Line

康託展開
詳見xly

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m,a[25],b[25];
ll fac[25];//階乘 
ll contor(){
	ll res=0;
	for(int i=1;i<=n;i++){
		int t=0;
		for(int j=i+1;j<=n;j++)if(a[j]<a[i])t++;
		res+=t*1ll*fac[n-i];
	}
	return res;
}
bool vis[30];
void reverse_contor(ll x){
	memset(vis,0,sizeof(vis));
	x--;
	for(int i=1;i<=n;i++){
		ll t=x/fac[n-i];
		int j;
		for(j=1;j<=n;j++){
			if(!vis[j]){
				if(!t)break;
				t--;
			} 
		}
		b[i]=j;
		vis[j]=1;
		x%=fac[n-i];
	}
}
int main(){
	cin>>n>>m;
	fac[0]=1;
	for(int i=1;i<=n;i++)fac[i]=fac[i-1]*1ll*i;
	while(m--){
		char c;
		cin>>c;
		if(c=='P'){
			ll q;
			cin>>q;//第K大 
			reverse_contor(q);
			for(int i=1;i<=n;i++)printf("%d ",b[i]);
			printf("\n"); 
		}
		else{
			for(int i=1;i<=n;i++)
				cin>>a[i];
			printf("%lld\n",contor()+1);
		}
	}
	return 0;
}

傳紙條

雙路DP
動態規劃
qwq

#include<bits/stdc++.h>
using namespace std;
int n,m,a[55][55];
int f[55][55][55];//一個人的橫縱座標和另一個人的橫座標 
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int e=1;e<=i+j-1&&e<=n;e++){
				if(i+j-e>m||(e==i&&(i!=n||j!=m)))continue;//!!!
				f[i][j][e]=max(max(f[i][j-1][e]/*雙左*/,f[i-1][j][e-1]/*雙上*/),max(f[i][j-1][e-1]/*左+上*/,f[i-1][j][e]/*上+左*/))+a[i][j]+a[e][i+j-e];
			}
	printf("%d",f[n][m][n]);
	return 0;
} 

OKR-Periods of Words

KMP
KMP的失配指標思想
?~ ?

#include<bits/stdc++.h>
#define ll long long
using namespace std;
char a[1000010];
int n,fail[1000010];
int main(){
    scanf("%d",&n);
    scanf("%s",a) ;
    int i,j;
    ll cnt=0;
    fail[0]=fail[1]=0;
    j=0;
    for(i=1;i<n;i++){//求解next???
        while(j&&(a[i]!=a[j]))
    		 j=fail[j];
        j+=(a[i]==a[j]);
        fail[i+1]=j;
    }
    for(i=1;i<=n;i++){
        j=i;
        while(fail[j])
       		 j=fail[j];
        if(fail[i]!=0) fail[i]=j;//記憶化
        cnt+=i-j;
    }
    printf("%lld",cnt);
}