您的位置:首页 > 其它

XDU2019寒假集训:week3 DP进阶

2019-02-12 10:19 253 查看

A:BZOJ 1264:[AHOI2006]

题意:给两个序列s1,s2,由N种元素构成,满足每个序列里各元素出现5次,求最长公共子序列 

思路:每个串中的元素都会重复五次,记录a串中的位置,读b串,因为已经知道两个相等,然后比较a串前面最大的加1和这个位置的值,用树状数组存一下区间最大值,树状数组+最长公共子序列变体

[code]#include<cstdio>
#define MAXN 100005
int n;
int f[MAXN],pos[MAXN/5][6];
int BIT[MAXN];
int max(int x,int y)
{
return x>y?x:y;
}
void init()
{
scanf("%d",&n);
n*=5;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
pos[x][++pos[x][0]]=i;
}
return ;
}
int lowbit(int x)
{
return x&(-x);
}
int query(int x)
{
int res=0;
for(int i=x;i;i-=lowbit(i))
res=max(res,BIT[i]);
return res;
}
int updata(int x,int val)
{
for(int i=x;i<=n;i+=lowbit(i))
BIT[i]=max(BIT[i],val);
return 0;
}
int solve()
{
int ans=0;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
for(int j=5;j>=1;j--)
{
int p=pos[x][j];
f

=max(f[p],query(p-1)+1);//因为是和前面的最大数进行 //比较,然后此位因为相等而+1 //所以此处p需要-1, updata(p,f[p]); //之前写的时候,居然把x和p写混了 //还是需要注意的 ans=max(ans,f[p]); } } return ans; } int main() { init(); printf("%d",solve()); return 0; }

[p]B:HDU 6537

题意:给一个由整数组成的序列,可以对区间[l,r]进行反转(invert),利用反转令区间的最长不降序子序列长度增大,求最后的序列长度和反转的区间

思路:第一个想的是先找升序列,再找降序列,然后两边加一下,结果发现10^5

C:zoj3537

题意:给出一些点表示多边形蛋糕的定点的位置(逆时针顺序),切蛋糕时每次只能在顶点和顶点间切,每一次切蛋糕都有相应的代价( |xi + xj| * |yi + yj| % p.),给出代价的公式,问把蛋糕切成多个三角形的最小代价是多少。顶点数<=300(最优三角剖分)

思路:判断凸包+区间dp

[code]#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define INF 2000000007
#define MAXN 305
using namespace std;
struct NOTE
{
int x;
int y;
};
NOTE poi[MAXN],sta[MAXN];
int ans[MAXN][MAXN];
int cost[MAXN][MAXN];
int n,top;
bool cmp(struct NOTE a,struct NOTE b)
{
double anga=atan2((a.y-poi[1].y),(a.x-poi[1].x));
double angb=atan2((b.y-poi[1].y),(b.x-poi[1].x));
if(anga!=angb)
return anga<angb;
return a.x<b.x;
}
ll det(NOTE a,NOTE b,NOTE c)
{
return 1LL*(b.x-a.x)*(c.y-a.y)-1LL*(c.x-a.x)*(b.y-a.y);
}
double dis(NOTE a,NOTE b)
{
return sqrt(1LL*(a.x-b.x)*(a.x-b.x)+1LL*(a.y-b.y)*(a.y-b.y));
}
void Graham()
{
struct NOTE tmp;
int pos=1;
tmp.x=INF,tmp.y=INF;
for(int i=1;i<=n;i++)
if(poi[i].y<tmp.y||(poi[i].y==tmp.y&&poi[i].x<tmp.x))
{
tmp=poi[i];pos=i;
}
swap(poi[pos],poi[1]);
sort(&poi[2],&poi[1+n],cmp);
sta[++top]=poi[1];
sta[++top]=poi[2];
//	printf("top==%d\n",top);
for(int i=3;i<=n;)
{
if(top>=0&&det(sta[top],sta[top-1],poi[i])>=0)
top--;
else
sta[++top]=poi[i++];
}
}
int Geta(int i,int j)
{
if(ans[i][j]!=-1)
return ans[i][j];
if(j-i<=2)
return 0;
ans[i][j]=INF;
for(int k=i+1;k<j;k++)
{

ans[i][j]=min(ans[i][j],Geta(i,k)+Geta(k,j)+cost[i][k]+cost[k][j]);
}
return ans[i][j];
}
int main()
{
int p;
while(~scanf("%d%d",&n,&p))
{
for(int i=1;i<=n;i++)
scanf("%d%d",&poi[i].x,&poi[i].y);
top=0;
Graham();
if(top<n)
{
printf("I can't cut.\n");
continue;
}
memset(ans,-1,sizeof(ans));
memset(cost,0,sizeof(cost));
for(int i=1;i<=n;i++)
for(int j=i+2;j<=n;j++)
cost[i][j]=cost[j][i]=(abs(poi[i].x+poi[j].x)*abs(poi[i].y+poi[j].y))%p;
printf("%d\n",Geta(1,n));
}
return 0;
}

M:POJ2823

题意:n个数,长度区间为k,求所有长度为k的区间内的最大值和最小值,

思路:经典的单调队列优化dp(没看见转移方程有点懵。)

所得:单调队列求前k大(小)效果拔群

[code]#include<cstdio>
#define MAXN 1000005
int n,k;
int num[MAXN];
int minn[MAXN*5],maxn[MAXN*5];
int min_q[MAXN],max_q[MAXN];
int pre_high,pre_low,tail_high,tail_low;
void init()
{
pre_high=pre_low=tail_high=tail_low=0;
}
void solve(int i)
{
int x=num[i];
while(tail_high>pre_high&&num[max_q[tail_high-1]]<x&&tail_high-1>=0)
{
tail_high--;
}
max_q[tail_high++]=i;
while(tail_low>pre_low&&num[min_q[tail_low-1]]>x&&tail_low-1>=0)
{
tail_low--;
}
min_q[tail_low++]=i;
if(i>=k)
{
if(min_q[pre_low]<i+1-k&&pre_low<=tail_low)
pre_low++;
minn[i+1-k]=num[min_q[pre_low]];
if(max_q[pre_high]<i+1-k&&pre_high<=tail_high)
pre_high++;
maxn[i+1-k]=num[max_q[pre_high]];
}
}
void print(int *a)
{
for(int i=1;i<=n-k+1;i++)
{
printf("%d%c",a[i],i==n-k+1?'\n':' ');
}
}
int main()
{
scanf("%d%d",&n,&k);
init();
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
solve(i);
}
print(minn);
print(maxn);
}

其他方法:在网上看优先队列,线段树也都可以做,有时间补

T:

题意:给定区间n~m,求其间数字不包含4和62的数字数量

思路:觉得暴力打表好写就写了居然过了。查了一下发现正解是数位dp(讲课的ppt里有),之后再学下数位dp补一下

学完之后U应该也能过了

[code]//打表代码
#include<cstdio>
#define MAXN 1000005
int ans[MAXN];
void init()
{
for(int i=1;i<=1000000;i++)
{
int tmp=i;
while(tmp)
{
if(tmp%10==4 || tmp% 100==62)
{
ans[i]=1;
break;
}
tmp/=10;
}
ans[i]+=ans[i-1];
}
}
int main()
{
int l,r;
init();
while(scanf("%d%d",&l,&r))
{
if(!(l&&r))
break;
printf("%d\n",r+1-l-(ans[r]-ans[l-1]));
}
}

U:

题意:求1~n之间有多少个数又含有13又能被13整除,(1<=n<=1e9)

思路:数位dp,具体程序注释

[code]#include<cstdio>
#include<cstring>
#define MAXN 20
int dp[MAXN][MAXN][10];
int maxx[MAXN];
int Geta(int len,int mod,int stu,bool lim)
{
if(len==0)//当所有位置上的数字列举完成之后
return mod==0&&stu==2;
if(!lim && dp[len][mod][stu])
return dp[len][mod][stu];
int cnt=0,max=lim?maxx[len]:9;
for(int i=0;i<=max;i++)
{
int ns=stu;
//状态的转换
if(stu!=2&&i==1)//如果数字没有13,并且此位为1,那么标记为状态1
ns=1;
if(stu!=2&&i!=1)//如果此位不为1,那么就什么都没有,即为0
ns=0;
if(stu==1&&i==3)//如果前一位是1,并且这一位是3的话,这个数字就拥有了13
ns=2;
//注意顺序
cnt+=Geta(len-1,(mod*10+i)%13,ns,lim&&i==max);
}
if(!lim)//没有超过的话,就将计数返还
dp[len][mod][stu]=cnt;
return cnt;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
memset(dp,0,sizeof(dp));
int len=0;
while(n)
{
maxx[++len]=n%10;
n/=10;
}

printf("%d\n",Geta(len,0,0,1));
}
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: