1. 程式人生 > 實用技巧 >劍指 Offer 31. 棧的壓入、彈出序列 - 8月11日

劍指 Offer 31. 棧的壓入、彈出序列 - 8月11日

A. Suborrays

題解

構造一個序列使得任意區間按位或的答案大於等於區間長度,打個表發現好像啥都行,於是就瞎輸出了

#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
int T,n;
int main()
{
	T=read();
	while(T--)
	{
		n=read();
		for(int i=1;i<n;i++)printf("%d ",i);
		printf("%d\n",n);
	}
	return 0;
}

B. Fix You

題解

一開始讀錯題了以為LRUD都有,結果發現只有RD,於是就只需要判下面是不是都是R,右邊是不是都是D就好了

#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
int n,m,a[110][110];
char pic[110][110];
bool vis[110][110];
bool legal(int x,int y)
{
	return (x>=0 && x<n) && (y>=0 && y<m);
}
queue<PII> Q;
int main()
{
	for(int T=read();T;T--)
	{
		n=read();m=read();
		for(int i=0;i<n;i++)for(int j=0;j<m;j++)vis[i][j]=a[i][j]=0;
		for(int i=0;i<n;i++)scanf("%s",pic[i]);
		int cnt=0;
		for(int i=0;i<m-1;i++)if(pic[n-1][i]!='R')cnt++;
		for(int i=0;i<n-1;i++)if(pic[i][m-1]!='D')cnt++;
		printf("%d\n",cnt);
	}
	return 0;
}

C. Cyclic Permutations

題解

題目花裡胡哨的說了一坨,反正推了一下發現滿足題意的就是這個序列需要有一個谷底,那麼我們反其道行之,求不存在谷底的排列有多少就行,設峰值\(n\)\(i\) 位置,那麼我們剩下\(n-1\)個數就已經按順序拍好了,有\(C_{n-1}^{i-1}\) 種方案,最後用\(n!\) 減去\(\sum_{i=0}^{n-1} C_{n-1}^i\) 即可,(其實那玩意就是\(2_{n-1}\) 沒有發現)

#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
const int MOD=1000000007,maxn=1000010;
int n;
LL fac=1,inv[maxn],C[maxn],ans; 
int main()
{
	n=read();
	for(int i=1;i<=n;i++)fac=(fac*i)%MOD;	
	inv[1]=1;
	for(int i=2;i<=n;i++)inv[i]=((MOD-MOD/i)*inv[MOD%i])%MOD;
	C[0]=1;
	for(int i=1;i<=n-1;i++)C[i]=(C[i-1]*LL(n-i))%MOD*inv[i]%MOD;
	for(int i=1;i<=n;i++)ans=(ans+C[i-1])%MOD;
	printf("%lld\n",((fac-ans)%MOD+MOD)%MOD);
	return 0;
}

D. 505

題解

題目大意是給你一個01矩陣,問最少修改多少個數,使得這個矩陣的所有偶數邊長的子正方形矩陣裡面都有奇數個1。
很容易發現,當\(n>3\)的時候,存在一個\(4*4\) 的矩陣,裡面還包含著4個\(2*2\) 的矩陣,這個肯定是矛盾的,所以就只討論\(n<4\) 的情況。
\(n=1\) 的時候答案是0, 當\(n=2或者n=3\) 的時候就狀壓DP設\(dp[i][s]\) 表示當前在第\(i\) 列,第\(i\) 列的01擺法為S就可以了,複雜度是\(O(4*4*n/2)或者O(8*8*n/6)\)

#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
const int maxn=1000010,inf=maxn;
int n,m,a[4][maxn],dp[maxn][2][2],DP[maxn][8];
char s[4][maxn];
int main()
{
	n=read();m=read();
	if(n>=4)return puts("-1"),0;
	if(n==1)return puts("0"),0;
	for(int i=0;i<n;i++)scanf("%s",s[i]);
		for(int i=0;i<n;i++)
			for(int j=0;j<m;j++)a[i][j]=s[i][j]-'0';
	if(n==2)
	{
		for(int i=0;i<m;i++)dp[i][0][0]=dp[i][0][1]=dp[i][1][0]=dp[i][1][1]=inf;
		for(int i=0;i<2;i++)
			for(int j=0;j<2;j++)
				if(a[0][0]==i && a[1][0]==j)dp[0][i][j]=0;
				else dp[0][i][j]=1;
		for(int i=1;i<m;i++)
		{
			for(int jj=0;jj<2;jj++)
				for(int kk=0;kk<2;kk++)
				{
					int cnt=2;
					if(a[0][i]==jj)cnt--;
					if(a[1][i]==kk)cnt--;
					for(int j=0;j<2;j++)
						for(int k=0;k<2;k++)
							if((j+k+jj+kk)%2)dp[i][jj][kk]=min(dp[i][jj][kk],dp[i-1][j][k]+cnt);
				}
		}
		int ans=maxn;
		for(int i=0;i<2;i++)for(int j=0;j<2;j++)ans=min(ans,dp[m-1][i][j]);
		printf("%d\n",ans);
		return 0;
	}
	if(n==3)
	{
		for(int i=0;i<m;i++)for(int j=0;j<8;j++)DP[i][j]=inf;
		for(int i=0;i<8;i++)
		{
			int S1=i&1,S2=(i>>1)&1,S3=(i>>2)&1;
			int cnt=3;
			if(a[0][0]==S1)cnt--;
			if(a[1][0]==S2)cnt--;
			if(a[2][0]==S3)cnt--;
			DP[0][i]=cnt;
		}
		for(int i=1;i<m;i++)
		{
			for(int S=0;S<8;S++)
			{
				int S1=S&1,S2=(S>>1)&1,S3=(S>>2)&1;
//				printf("%d %d%d%d\n",S,S1,S2,S3);
				int cnt=3;
				if(a[0][i]==S1)cnt--;
				if(a[1][i]==S2)cnt--;
				if(a[2][i]==S3)cnt--;
				for(int s=0;s<8;s++)
				{
					int s1=s&1,s2=(s>>1)&1,s3=(s>>2)&1;
//					if(i==1 && S==6)printf("%d %d %d %d %d %d\n",s1,s2,s3,S1,S2,S3);
					if((S1+s1+S2+s2)%2 && (S2+s2+S3+s3)%2)DP[i][S]=min(DP[i][S],DP[i-1][s]+cnt);
				}
//				printf("%d %d %d\n",i,S,DP[i][S]);
			}
		}
//		for(int i=0;i<m;i++)for(int S=0;S<8;S++)printf("%d %d %d\n",i,S,DP[i][S]);
		int ans=maxn;
		for(int i=0;i<8;i++)ans=min(ans,DP[m-1][i]);
		printf("%d\n",ans);
	}
	return 0;
}

E. Pairs of Pairs

題解

又是一道很詭異的題,題目大意是給你一個無向圖,你要不在圖中找到一個長度大於等於\(\lfloor \frac{n}{2} \rfloor\) 的簡單路徑,要不就找若干個二元組(其中這些二元組的元素不能重複,至少要多於等於\(\lfloor \frac{n}{2} \rfloor\) ),使得任意兩個二元組組成的子圖,至多有兩條邊。
直接看了題解,題解是隨便找一點開始求他的dfs樹,如果dfs樹中有深度大於等於\(\lfloor \frac{n}{2} \rfloor\) 的點,就直接輸出即可,否則就找所有深度相同的點,兩兩配對,一定能保證配出\(\lfloor \frac{n}{4} \rfloor\) 對。怎麼證明呢?
先證這任意兩個二元組組成的子圖吧,根據dfs樹的性質,所有非樹邊一定是祖先連往子孫,不可能存在子樹之間的連邊 ,深度相同的點顯然是屬於兩個子樹,所以保證兩個二元組\((a,b),(c,d)\) 其中\(deep[a]=deep[b],deep[c]=deep[d]\)\(deep[a]<deep[c]\),a和b 不可能連邊,c和d不可能連邊,那兩條邊只可能是a和c,b和d為父子關係的時候才可以算上。
再證一定大於\(\lfloor \frac{n}{4} \rfloor\) 對,因為不存在深度大於\(\lfloor \frac{n}{2} \rfloor\) 了,所以最大深度不過\(\lfloor \frac{n}{2} \rfloor\) ,每一個深度只有當有0個或者1個節點的時候才會放棄這個深度,根據抽屜原理一定能選出\(\lfloor \frac{n}{4} \rfloor\) 對滿足。

#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
const int maxn=500010,maxm=1000010;
int n,m,a,b,deep[maxn],pa[maxn],max_deep;
vector<int> G[maxn],deep_pair[maxn];
bool path,vis[maxn];
void dfs(int now,int fa)
{
	vis[now]=1;
	pa[now]=fa;
	if(path)return;
	deep_pair[deep[now]].push_back(now);
	max_deep=max(max_deep,deep[now]);
	if(deep[now]>=(n+1)/2)
	{
		int tmp=now;
		printf("PATH\n%d\n",deep[now]);
		while(tmp!=1)printf("%d ",tmp),tmp=pa[tmp];
		puts("1");
		path=1;
		return;
	}
	for(int v:G[now])
		if(v!=fa && !vis[v])
			deep[v]=deep[now]+1,dfs(v,now);
}
int main()
{
	for(int T=read();T;T--)
	{
		n=read();m=read();
		max_deep=path=0;
		for(int i=1;i<=n;i++)G[i].clear(),vis[i]=0;
		for(int i=1;i<=m;i++)a=read(),b=read(),G[a].push_back(b),G[b].push_back(a);
		deep[1]=1; 
		dfs(1,0);
		if(!path)
		{
			int cnt=0;
			for(int i=2;i<=max_deep;i++)cnt+=deep_pair[i].size()/2;
			printf("PAIRING\n%d\n",cnt);
//			for(int i=1;i<=max_deep;i++)
//			{
//				printf("deep %d:",i);
//				for(int j=0;j<deep_pair[i].size();j++)printf("%d ",deep_pair[i][j]);
//				puts("");
//			}
			for(int i=2;i<=max_deep;i++)
			{
				for(int j=0;j+1<deep_pair[i].size();j+=2)printf("%d %d\n",deep_pair[i][j],deep_pair[i][j+1]);
			}
		}
		for(int i=1;i<=max_deep;i++)deep_pair[i].clear();
	} 
	return 0;
}

廢話