首页 > 其他分享 >9.27

9.27

时间:2022-09-27 21:47:49浏览次数:47  
标签:9.27 int 50 fail include dp dis

T1 生日快乐

题目传送门
虽然是水题,但很容易被误导成二分,没有证明的直觉不是直觉,但是题目没有卡这种错误的做法
拿了\(70pts\),和二分相近,只不过每次将所有可能的情况都分出来
也就是说 , 因为只能平行与两边进行切,所以每次\(DFS\)时将可能出现的情况枚举即可

点击查看代码
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
int n;
double x,y,ans=1e9;
double dfs(double x,double y,int d)
{
	if(x<y) swap(x,y);
//	cout<<x<<"   "<<y<<"   "<<d<<endl;
	if(d==1)
	{
		return x/y;
	}
	double mx=x/d,my=y/d,ans=1e9;// 每次在搜索时更新ans值,不能使用全局,每种方法都有不同分法 
	double ans1,ans2;
	for(int i=1;i<=d/2;i++) // 以单位块增加 即枚举所有切法 
    {
        ans1=max(dfs(mx*i,y,i),dfs(x-mx*i,y,d-i));//  沿着x 方向切  i块 与tot-i块 
        ans2=max(dfs(x,my*i,i),dfs(x,y-my*i,d-i));
        ans=min(ans,min(ans1,ans2));
    }
	return ans;
}
signed main()
{
//	freopen("happy.in","r",stdin);
//	freopen("happy.out","w",stdout);
	scanf("%lf%lf%ld",&x,&y,&n);
	;
	printf("%.6f",dfs(x,y,n));
	return 0;
}

T2 最远距离

题目传送门
这个题意也有点日龙,意思就是一个点可以通过上下左右方向且不被阻挡到达最远的另一个点
然后求出两点间直线距离
虽然题面上是说最远距离,但我们不妨转化为一个最短路问题;
\(dis[i][j]\)表示从某一点到\((i,j)\)所需要移动的障碍
因为数据很小可以用\(SPFA\)水过
然后每次枚举起始点,再枚举终止点,记录直线距离最大值;

点击查看代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
int  n,m,t;
int dx[4]={0, 0, 1, -1}, dy[4]={1, -1, 0, 0};

struct node{int x,y;};

node make(int x,int y)
{
	node st; st.x=x; st.y=y;
	return st;
}

bool ck(int x,int y)
{
	if(x<=0 || x>n || y<=0 || y>m) return false;
	return true;
}

int dis[50][50],G[50][50],vis[50][50];
int X,Y;

void spfa(int x, int y)
{
	queue<node>q;
	memset(dis, 127, sizeof dis);
	dis[x][y]=G[x][y];
	node st;
	q.push(make(x,y));
	while (q.size())
	{
		int xx=q.front().x, yy=q.front().y;
		vis[xx][yy]=false;
		q.pop();
		for (int i=0; i<4; i++)
		{
			X=xx+dx[i]; Y=yy+dy[i];
			if (!ck(X,Y)) continue;
			
			if (dis[X][Y]>dis[xx][yy]+G[X][Y])
			{
				dis[X][Y]=dis[xx][yy]+G[X][Y];
				if (!vis[X][Y])
				{
					q.push(make(X, Y));
					vis[X][Y]=true;
				}
			}
		}
	}
}

char s[50];
double ans;
signed  main()
{
//	freopen("dis.in","r",stdin);
//	freopen("dis.out","w",stdout);
	scanf("%ld%ld%ld",&n,&m,&t);
	for(int i=1;i<=n;i++)
	{
		cin>>s+1;
		for(int j=1;j<=m;j++)
		{
			G[i][j]=(s[j]=='1');
		}
	}
	
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			
			spfa(i,j);
			
			for(int x=1;x<=n;x++)
			{
				for(int y=1;y<=m;y++)
				{
					if(dis[x][y]<=t)
					ans=max(ans,sqrt(abs(x-i)*abs(x-i)+abs(y-j)*abs(y-j)));
				}
			}
		}
	}
	printf("%.6f\n",ans);
	return 0;
}

T3 windy数

题目传送门
妥妥的数位\(DP\)题,用的记忆化搜索,详情见代码

点击查看代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define int long long
#define IOS   std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
int s[30],dp[30][30];
int dfs(int pos, int pre, int f, int lim )// 当前位数 原先上一位数 是否有前导0 是否有最高位限制 
{
	if(!pos)return 1;// 如果已经没有数位可以枚举了 返回  1 
	if(!f && dp[pos][pre] != -1 && !lim) return dp[pos][pre];// 记忆化搜索 如果没有前导0 并且当前值已经存在 并且没有最高位限制 
	int up = lim ? s[pos] : 9;// 最高位限制判断 
	int ans = 0;
	for(int i=0;i<=up;i++) // 枚举位数上的可能情况 
	{
		int tmp=abs(pre-i);
		if(!f && tmp < 2 )continue;// 判断是否合法 
		ans+=dfs( pos-1, i, f&&!i, lim&&i==up );// 继续搜索下一位 记录当前位(即下一次搜索的pre) 看是否还有前导0 与最高位限制 
	}
	if(!lim && !f)dp[pos][pre] = ans;// 记忆化搜索 记录答案 
	return ans;
}
int solve(int x)
{
	int cnt=0;
	memset(s,0,sizeof s);
	while(x)
	{
		s[++cnt]=x%10;
		x/=10;
	}
	return dfs(cnt,-1,1,1);// 因为上一位不存在 所以最高位只要在1~最高位之间 
}
int n,m;
signed  main()
{
//	freopen("windy.in","r",stdin);
//	freopen("windy.out","w",stdout);
	scanf("%ld%ld",&n,&m);
	memset(dp,-1,sizeof dp);
	printf("%lld",solve(m)-solve(n-1));
	return 0;
}

T4 GT考试

题目传送门
因为有了上一道数位\(DP\),所以这一道也想数位\(DP\),然后就挂了

分析

首先 ,看到匹配,显然可以 \(kmp\) 解决
然后 ,是求方案数,显然可以 \(DP\) 递推
怎么递推??不会力
在上了个厕所,遭遇宇宙射线后,我们知道了答案

\(dp[i][j]\)表示当前在第 \(i\) 位时,末尾已经匹配了 \(j\) 位不吉利串
显然答案最后为\(dp[n][j]=\sum_{k=0}^{m-1}dp[n-1] [k]\)
再看转移\(dp[i]\)是由\(dp[i-1]\)在添加一个数后转移,
添加一个数后会有三种情况

1.当前数位又向着不吉利串更近一步(成功匹配到了下一位成为一段更长的不吉利子串,如12121 为121变为1212)
2.摆脱不吉利串(失配如 1111 变为 1112)
3.摆脱但没完全摆脱(如从1211变为1212,后面的12有可能变为1211)

这就需要\(kmp\)进行维护
用一个\(fail[i][j]\)记录从不吉利串 \(i\) 位匹配到 \(j\) 位的方案数
比如\(12\)到\(121\)(\(fail[2][3]=1\))只可能一种情况
如\(12\) 到 \(1\) (\(fail[2][1]=9\))会有\(9\)种可能
所以\(dp[i][j]=\sum_{k=0}^{m-1}dp[n-1] [k]+fail[k][j]\)
然后发现\(i\)居然达到了惊人的 \(10^9\)

显而易见,请出矩阵加速
\(dp[i][j]=\sum_{k=0}^{m-1}dp[n-1] [k]+fail[k][j]\)

这不就是一个矩阵乘法的式子吗... 因为\(fail[i][j]\)是固定的,于是把\(f[i][j]\)看成一个矩阵,对于矩阵\(dp[i]\)它的第一层就是\(dp[i][j]\)

\(fail\)是固定的,你又知道\(dp[0]\)的第一行第一列是1,然后求一个\(dp[n]\)就行了... 矩阵快速幂即可....

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define maxn 100010
using namespace std;
int n,m,K;
struct node
{
	int a[50][50];
	node ()
	{
		memset(a,0,sizeof(a));
	}
};

int nx[50],fail[50][50];
void kmp(char* s)
{
	
	nx[1]=0;
	for(int i=2,j=0;i<=m;i++)
	{
		while(j&&s[j+1]!=s[i]) j=nx[j];
		if(s[i]==s[j+1]) j++;
		nx[i]=j;
	}
	
	for(int i=0;i<m;i++)
	{
		for(int j='0';j<='9';++j)
		{
			int tmp=i;
			while(tmp&&s[tmp+1]!=j) tmp=nx[tmp];
			if(s[tmp+1]==j) tmp++;
			if(tmp<m) fail[i][tmp]++;
		}
	}
	
}

node mul(node a,node b)
{
	node c;
	for(int i=0;i<50;i++)
		for(int j=0;j<50;j++)
			for(int k=0;k<50;k++)
			{
				c.a[i][j]=(c.a[i][j]+a.a[i][k] * b.a[k][j]%K)%K;		
			}
	return c;
}

node ksm(node a,int b)
{
	node ans;
	for(int i=0;i<=m;i++) ans.a[i][i]=1;
	
	for(;b;b>>=1)
	{
		if(b&1) ans=mul(ans,a);
		a=mul(a,a);
	}
/*	for(int i=0;i<=m;i++)
	{
		for(int j=0;j<=m;j++) cout<<ans.a[i][j]<<" ";
		cout<<endl;
	}*/
	return ans;
}

char s[50];
signed main()
{
	scanf("%lld%lld%lld",&n,&m,&K);
	
	cin>>s+1;
	kmp(s);
	
	node Ans,qwq,Qwq;
	Ans.a[0][0]=1;
	
/*	for(int i=0;i<=m;i++)
	{
		for(int j=0;j<=m;j++) cout<<fail[i][j]<<" ";
		cout<<endl;
	}*/
		
	for(int i=0;i<m;i++)
		for(int j=0;j<m;j++)  qwq.a[i][j]=fail[i][j];
		
/*	for(int i=0;i<=m;i++)
	{
		for(int j=0;j<=m;j++) cout<<qwq.a[i][j]<<" ";
		cout<<endl;
	}*/
		
	Qwq=ksm(qwq,n);
	
/*	for(int i=0;i<=m;i++)
	{
		for(int j=0;j<=m;j++) cout<<Qwq.a[i][j]<<" ";
		cout<<endl;
	}*/
	Ans=mul(Ans,Qwq);
	
/*	for(int i=0;i<=m;i++)
	{
		for(int j=0;j<=m;j++) cout<<Ans.a[i][j]<<" ";
		cout<<endl;
	}*/
	
	
	int ans=0;
	for(int i=0;i<m;i++) ans=(ans+Ans.a[0][i])%K;
	printf("%lld\n",ans);
    return 0;
}

标签:9.27,int,50,fail,include,dp,dis
From: https://www.cnblogs.com/llwwll/p/16736079.html

相关文章

  • 闲话 22.9.27
    闲话似乎没几个人把闲话设为显示但是我除了闲话没啥了所以不隐藏了而且主要的简介都在摘要里面了我等一等再去切risrqnis我还剩五个点和55pts(今天想到拉格朗日的......
  • 9.27
    今日内容1.垃圾回收机制2.流程控制理论3.流程控制之分支结构4.流程控制之循环结构垃圾回收机制"""有一些语言内存空间的申请和释放都需要程序员自己写代码才可以完......
  • 【闲话】2022.09.27闲话
    考试++今天大型机惨活动。明明只是去做了次核酸,怎么会变成这个样子呢?今天大型BA讨论活动L:泰国歌曲据说今晚\(\textrm{luogu}\)要修RMJ,希望明天会更好。......
  • 9.27比赛记录
    T1不会,再见。/fnT2题意有\(n\)个奶牛排成一列,每次队头的牛都会插到第倒数\(c_i\)个位置上,问有多少个牛无法到达第一位。思路是道很厉害的二分。可惜赛时没打出来......