20190718 DP练习赛

A

问题描述:密文的加密方式选中前 k 个字符作为根串,k > 4。然后在根串后添加长度为2或3的后缀串,当前添加的不能和上一次相等。现在给出一个最终的串,问有多少满足条件的后缀串,按字典序输出所有合法串。
5 ≤ n ≤ 100 for 40%
5 ≤ n ≤ 10000 for 100%
n为给出串的长度。

input
abcdefghij
output
5
fg
fgh
gh
hij
ij

题解:注意到后缀串的长度只可能是2或3,f[ i ][ 0/1 ] 表示以 第 i 个开始的长度为2 或 3 的后缀串是否合法。当前位置长度2能否合法,需要满足以下条件中的一个,i+2 位置长度为 3 的后缀串合法,i + 2 位置长度为 2 的后缀串合法且s[ i ] !=s[ i +2 ] || s[ i+1 ]!=s[ i+3 ]。长度为3的同理,因此枚举的顺序也是从后往前。以及最后一个长度为2或3的串的特判。

code

#include<bits/stdc++.h>
#define MAXN 10010
#define FLIE "suffix"
#define pb(x) push_back(x)
  using namespace std;
string s;
string X[MAXN*2];
int n;
int f[MAXN*2][2];
vector<string>M;
int main()
{
  freopen(FLIE".in","r",stdin);
  freopen(FLIE".out","w",stdout);
	cin>>s;
	n=s.size();
	for(int i=n-1;i>=5;i--)
	{
		if(i+2==n) f[i][1]=1;
		if(i+3==n) f[i][0]=1;
		if(i+2<n)
		{
		 if(f[i+2][0]) f[i][1]=1;
		 if(f[i+2][1]&&(s[i]!=s[i+2]||s[i+1]!=s[i+3])) f[i][1]=1;
		}
		if(i+3<n)
		{
		 if(f[i+3][1]) f[i][0]=1;
		 if(f[i+3][0]&&(s[i]!=s[i+3]||s[i+1]!=s[i+4]||s[i+2]!=s[i+5])) f[i][0]=1;
		}
	}
	for(int i=5;i<n;i++)
	{
	  string s1=""; s1+=s[i]; s1+=s[i+1]; 
	  if(f[i][1]) M.pb(s1); 
	  if(f[i][0]) s1+=s[i+2],M.pb(s1);
	}
	sort(M.begin(),M.end());
	int len=unique(M.begin(),M.end())-M.begin();
	printf("%d\n",len);
	for(int i=0;i<len;i++) cout<<M[i]<<endl;
  return 0;
}

B

问题描述:在给定的长度为 n 的序列中选出某些数组成新数列。满足:1 严格单调增 2相邻两数不互质。问满足的序列的最长长度。
1 ≤ ai ≤ 200000
1 ≤ n ≤ 1000 for 40%
1 ≤ n ≤ 100000 for 100%

input
9
1 2 3 5 6 7 8 9 10
output
4

题解:对序列排序,有一个朴素的转移方程。f[ i ] = max(f[ i ] , f[ j ] + 1) 1<= j <i
gcd(a[ i ], a[ j ] )!=1。复杂度O(n*k)。每次枚举当前的数的因数,从前面已经处理出来包含该因数的序列长度更新当前的点,并更新现在这个因数的序列长。复杂度O(n * sqrt( n ))
code:

#include<bits/stdc++.h>
#define FLIE "sequence"
#define MAXN 100010
 using namespace std;
int n,a[MAXN],ans=-1;
map<int,int>TONG; 
int f[MAXN];
int gcd(int x,int y) {if(!y) return x; else return gcd(y,x%y);}
int main()
{
  freopen(FLIE".in","r",stdin);
  freopen(FLIE".out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	
	for(int i=1;i<=n;i++)
  {
   f[i]=1;	
   for(int j=2;j*j<=a[i];j++)
   {
   	if(a[i]%j==0)  f[i]=max(f[i],max(TONG[j]+1,TONG[a[i]/j]+1));
	 }
	 f[i]=max(f[i],TONG[a[i]]+1);
	 for(int j=2;j*j<=a[i];j++)
   {
    if(a[i]%j==0) TONG[j]=max(TONG[j],f[i]),TONG[a[i]/j]=max(TONG[a[i]/j],f[i]);
	 }
	 TONG[a[i]]=max(TONG[a[i]],f[i]);
	}
	for(int i=1;i<=n;i++) ans=max(ans,f[i]);
	cout<<ans; 
  return 0;
}

C

问题描述:给定一个长度为 n 的序列,拥有不超过 s 次操作,每次操作的内容为交换相邻两个数。操作后前 k 个数最小为多少。
1 ≤ s ≤ 10^9
1 ≤ k ≤ n ≤ 50 for 40%
1 ≤ k ≤ n ≤ 150 for 100%
序列中的数 <=1000000

input
5 4 2
10 1 6 2 5
output
18

题解: 若 s > n*(n-1)/2 完全可以办到把最小前 k 放到。考虑状态 f[ i ][ j ] [ k ] 表示 前 i 个数中 已将 j 个换到且耗费 k 步。每次的 i 只有选和不选两种情况。转移方程 为 f[ i ][ j ][ k ] = min( f[ i-1 ][ j -1 ][ k-( i - j ) ] )

code

#include<bits/stdc++.h>
#define MAXN 155
#define FLIE "bodyguard"
 using namespace std;
int n,kk,s;
int f[2][MAXN][MAXN*MAXN];
int a[MAXN];
int ans=1e8;
int main()
{
  freopen(FLIE".in","r",stdin);
  freopen(FLIE".out","w",stdout);
  cin>>n>>kk>>s;
  for(int i=1;i<=n;i++) scanf("%d",&a[i]);	
	memset(f,10,sizeof(f));
	f[0][0][0]=0;
	for(int i=1;i<=n;i++)
	 for(int j=0;j<=min(i,kk);j++)
	  for(int k=0;k<=min(s,n*(n-1)/2);k++)
	  {
	   f[i&1][j][k]=f[i-1&1][j][k];
	   if(j&&k>=(i-j))
	  	f[i&1][j][k]=min(f[i&1][j][k],f[i-1&1][j-1][k-(i-j)]+a[i]);
		}
	 for(int k=0;k<=min(s,n*(n-1)/2);k++)
	  ans=min(ans,f[n&1][kk][k]);
	cout<<ans;
  return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值