[ACM刷题] DP数论专题

91 篇文章 1 订阅
42 篇文章 0 订阅

A:

在这里插入图片描述
求B对P的逆元就好了www

#include <iostream>
#include <cstdio>
#define int long long
const int mod=9973;
int inv,p,s;
int exgcd(int a,int b,int &x,int &y)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    int ans=exgcd(b,a%b,x,y);
    int tmp=x;
    x=y;
    y=tmp-a/b*y;

    return ans;
} 
void work()
{
	int n,b;
	scanf("%lld%lld",&n,&b);
	exgcd(b,mod,inv,s);
	inv=(inv+mod)%mod;
	printf("%lld\n",(inv*n)%mod);
}
signed main()
{
	int T;
	scanf("%lld",&T);
	while(T--) work();
	return 0;
}

B:

在这里插入图片描述存前三位后三位然后快速幂就完事了

#include <iostream>
#include <cstdio>
#define il inline
il double devs(double x)
{
	while(x>=1000.0) x/=10;
	return x;
}
il int ksm1(int x,int y)
{
	x%=1000;
	int ans=1;
	while(y)
	{
		if(y%2) ans=(ans*x)%1000;
		x=(x*x)%1000;
		y/=2;
	}
	return ans;
}
il int ksm2(double x,int y)
{
	x=devs(x);
	double ans=1;
	while(y)
	{
		if(y%2) ans=devs(ans*x);
		x=devs(x*x);
		y/=2;
	}
	return (int)devs(ans);
}
int main()
{
	int T;
	scanf("%d",&T);
    for(int i=1;i<=T;i++)
    {
    	int n,k;
    	scanf("%d%d",&n,&k);
    	printf("Case %d: %d %03d\n",i,ksm2((double)n,k),ksm1(n,k));
    }
	return 0;
}

C:

在这里插入图片描述设最小步数为t
那么 (x+tm)%L=(y+tn)%L
那么 t*(m-n)≡(y-x)modL 然后 ( m − n ) ∗ t + k ∗ L = y − x (m-n)*t+k*L=y-x (mn)t+kL=yx
然后套个拓欧解方程…

#include <cstdio>
#define ll long long
ll x,y,m,n,a,b,c,l;
ll e_gcd(ll a,ll b)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    ll ans=e_gcd(b,a%b);
    ll tmp=x;
    x=y;
    y=tmp-a/b*y;
    return ans;
} 
int main()
{
    scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&l);
    a=m-n;b=l;c=y-x; 
    if(a<0) a=-a,c=-c; 
    ll g=e_gcd(a,b);
    if(c%g) printf("Impossible\n");
    else printf("%lld\n",(c/g*x%(b/g)+b/g)%(b/g));
    return 0;
} 

D:

在这里插入图片描述这…模拟一下就过了

#include <iostream>
#include <cstdio>
int f1,f2,n;
int work()
{
	int ans=2;
	while(1)
	{
		int t=f1+f2;
		f1=f2;
		f2=t;
		if(f2<=n) ans++;
		else break;
	}
	return ans;
}
int main()
{
	while((scanf("%d%d%d",&f1,&f2,&n))!=EOF) printf("%d\n",work());
	return 0;
}

E:

在这里插入图片描述首先因为X,Y不能相同,所以每个木块只有可能有6个放法,把他们都搞出来,存起来,然后大的在下Dp就好了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
const int maxm=100;
struct node{
	int x,y,z;
}h[maxm*6];
int maxh[maxm*6];
int n;
int ts=0;
bool comp(node x1,node x2)
{
	if(x1.x==x2.x) return x1.y>x2.y;
	return x1.x>x2.x;
}
void work()
{
	int ans=-1;
	memset(h,0,sizeof(h));
	memset(maxh,0,sizeof(maxh));
	int tot=0;
	for(int i=1;i<=n;i++)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		h[++tot].x=a,h[tot].y=b,h[tot].z=c;
		h[++tot].x=a,h[tot].y=c,h[tot].z=b;
		h[++tot].x=b,h[tot].y=a,h[tot].z=c;
		h[++tot].x=b,h[tot].y=c,h[tot].z=a;
		h[++tot].x=c,h[tot].y=a,h[tot].z=b;
		h[++tot].x=c,h[tot].y=b,h[tot].z=a;
	}
	std::sort(h+1,h+tot+1,comp);
	for(int i=1;i<=tot;i++) maxh[i]=h[i].z;
	for(int i=2;i<=tot;i++)
	{
		int plus=0;
		for(int j=1;j<i;j++)
		 if(maxh[j]>plus)
		  if((h[j].x>h[i].x)&&(h[j].y>h[i].y)) plus=maxh[j];
		maxh[i]+=plus;
		ans=std::max(ans,maxh[i]);
	}
	printf("Case %d: maximum height = %d\n",ts,ans);
}
int main()
{
	while(1)
	{
		scanf("%d",&n);
		if(!n) return 0;
		ts++;
		work();
	}
}

F:

在这里插入图片描述
dp[l][r]表示把 l-r这些断点都砍断最低代价是多少
然后记忆化搜索就行了

#include <cstdio>
#include <iostream>
#include <cstring>
const int inf=1e9+7;
int dp[55][55];
int a[55];
int len,n;
int dfs(int l,int r)
{
	if(~dp[l][r]) return dp[l][r];
	if(l>=(r-1)) return dp[l][r]=0; 
	dp[l][r]=inf;
	for(int i=l+1;i<r;i++) dp[l][r]=std::min(dp[l][r],dfs(l,i)+dfs(i,r)+a[r]-a[l]);
	return dp[l][r];
}
int main()
{
	while(1)
	{
	  scanf("%d",&len);
	  if(!len) return 0;
	  scanf("%d",&n);
	  memset(dp,-1,sizeof(dp));
	  for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	  a[n+1]=len;
	  printf("The minimum cutting is %d.\n",dfs(0,n+1));
	}
	
	return 0;
}

G:

在这里插入图片描述
完全背包+二进制优化…

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
const int maxm=200005;
int s[7];
bool dp[maxm];
int w[maxm];
bool work()
{
	memset(dp,0,sizeof(dp));
	int p=0;
	for(int i=1;i<=6;i++) p+=s[i]*i;
	if(p%2) return 0;
	p/=2;
	dp[0]=1;
	int cnt=0;
	for(int i=1;i<=6;i++)
	{
		for(int j=1;j<=s[i];j*=2) w[++cnt]=j*i,s[i]-=j;
		if(s[i]>0) w[++cnt]=i*s[i];
	}
	for(int i=1;i<=cnt;i++)
	 for(int j=p;j>=w[i];j--)
	  if(dp[j-w[i]]) dp[j]=1;
	return dp[p];
}
int main()
{
	int t=0;
	while(1)
	{
		bool flag=0;
		for(int i=1;i<=6;i++)
		{
			scanf("%d",&s[i]);
			if(s[i]) flag=1;
		}
		if(!flag) return 0;
		t++;
		if(!work()) printf("Collection #%d:\nCan't be divided.\n\n",t);
		else printf("Collection #%d:\nCan be divided.\n\n",t);
	}
}

H:

在这里插入图片描述
dp[i]表示高度为i的方案数,然后递推就好了,感觉有点背包的意思,别忘了去掉全是高度为1的情况

#include <cstdio>
#include <iostream>
#include <cstring>
#define int long long
int dp[600];
signed main()
{
	int n;
	while(scanf("%lld",&n)&&n)
	{
		memset(dp,0,sizeof(dp));
		dp[0]=1;
		for(int i=1;i<=n;i++)
		 for(int j=n;j>=i;j--)
		  dp[j]+=dp[j-i];
		printf("%lld\n",dp[n]-1);
	}
	return 0;
}

I:

在这里插入图片描述
记忆化搜索…

#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
int t[105][105],x2,y2,maxf=-1,f[105][105];
int dx[5]={0,1,-1,0,0},dy[5]={0,0,0,-1,1};
int dfs(int x,int y)
{
     for(int i=1;i<=4;i++)
      {
      	int x3,y3;
      	x3=x+dx[i];
      	y3=y+dy[i];
      	if(x3>=1&&x3<=x2&&y3>=1&&y3<=y2)
      	{
            if(t[x][y]>=t[x3][y3]) continue;
      		if(f[x3][y3]!=1) f[x][y]=max(f[x][y],f[x3][y3]+1);
      		else f[x][y]=max(f[x][y],dfs(x3,y3)+1);
      	}
      }
      
      return f[x][y];
}
int main()
{
    scanf("%d%d",&x2,&y2);
    
    for(int i=1;i<=x2;i++)
     for(int j=1;j<=y2;j++)
      {
      	scanf("%d",&t[i][j]);
      	f[i][j]=1;
      }
      
     for(int i=1;i<=x2;i++)
      for(int j=1;j<=y2;j++)
      {
       if(f[i][j]!=1) maxf=max(maxf,f[i][j]);
       else           maxf=max(maxf,dfs(i,j));
      }
      
     printf("%d",maxf);
      
     return 0;
}

J:

在这里插入图片描述
区间dp
dp[i][j]表示i-j的最大匹配数
枚举i,j
如果i,j匹配,那么dp[i][j]=dp[i+1][j-1]+2
考虑不匹配 dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j])

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
const int maxm=1e5+100;
char s[maxm];
int dp[105][105];
bool ok(int x,int y){return (s[x]=='('&&s[y]==')')||(s[x]=='['&&s[y]==']');}
inline void work()
{
	memset(dp,0,sizeof(dp));
	int n=strlen(s+1);
	for(int i=n;i>=1;i--)
	 for(int j=i+1;j<=n;j++)
	 {
	 	if(ok(i,j)) dp[i][j]=dp[i+1][j-1]+2;
	 	for(int k=i;k<=j-1;k++) dp[i][j]=std::max(dp[i][j],dp[i][k]+dp[k+1][j]);
	 }
	printf("%d\n",dp[1][n]);
}
int main()
{
	while(scanf("%s",(s+1))&&s[1]!='e') work();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值