• 【暑假集训模拟DAY4】DP&递推


    前言

    之前没写,现在补上

    开局看题,T1暴力30pts秒出,但不甘心想尝试正解,结果数位DP没看出来的我自然是白给了

    T2期望DP,根本没练过,先跳过(最后没时间就打了一个固输得了10pts ovo)

    T3看似很复杂,实际上可以转化成普通的DP,但是没找到什么特殊的规律(还是太年轻),求出Floyd之后写了个暴力

    T4看到分值分布就觉得得分希望不大(两个subtask,分别是35pts和65pts),加上没有好的做法就放弃了

    期望得分:30+10+30+0=70pts

    实际得分:30+10+25+0=65pts

    好吧T3稍微TLE了一个点,也能接受

    虽然分低但好歹没挂分(这能挂了那我也是重量级)

    看题吧

    题解

    T1 lecture

    不会数位DP的小丑竟是我自己

    我应该是全机房唯一没看出来是数位DP的(这明明是数位DP裸题啊喂!),因为总共就做过两道,还是2个月前的某一天

    不过DP状态倒是和数位DP状态设的一模一样

    不过这题有点不同,从低位到高位好写一些,因为要判断后缀是否整除,会更方便一些

    复习了一下数位DP的写法,还是挺不错的(挖坑,还是要多练几道数位熟悉熟悉)坑已填好=w=

    (回看博客发现上面说了一堆废话)

    代码中用 zero 表示后导零有没有结束,check 表示有没有整除 k 的后缀,这些都是为了判断删除前缀之后后缀不能为0(例如:10000->0000是不可以的),zero 不同的时候记忆化的结果也不同,所以只记忆化zero==0的情况(因为zero==1的情况很少),可以写出以下代码

    代码:

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int INF = 0x3f3f3f3f;
    int f[1005][105][2],n,k,m,mi[1005];
    int dp(int pos,int res,int check,int zero)
    {
    	if(pos>n) return check;
    	if(!zero&&f[pos][res][check]!=-1) return f[pos][res][check];
    	int ans=0;
    	for(int i=0;i<=9;i++)
    	{
    		if(!i&&pos==n) continue;
    		int tmp=(res+i*mi[pos-1]%k)%k;
    		if(check) ans=(ans+dp(pos+1,tmp,check,zero&&!i))%m;
    		else ans=(ans+dp(pos+1,tmp,!tmp&&!(zero&&!i),zero&&!i))%m;
    	}
    	if(!zero) f[pos][res][check]=ans;
    	return ans;
    }
    int main()
    {
    	memset(f,-1,sizeof(f));
    	scanf("%d%d%d",&n,&k,&m);
    	mi[0]=1;
    	for(int i=1;i<=n;i++) mi[i]=(mi[i-1]*10)%k;
    	printf("%d",dp(1,0,0,1));
    	return 0;
    }
    

    T2 nthlon

    考场看到期望直接跪了好吗...直接弃了

    期望DP主要利用的也就是期望的线性性和对称性,还有期望和概率的关系

    翻译过来就是期望可以分别算加在一起,每个人的期望相同,算期望一般先算概率

    这里就是一个类似背包的转移,不过这里用了前缀和优化很巧妙,虽然看起来很简单但实际上不容易发现

    这里 m-1 个人的期望值相同,所以算一个人得分为 i 的概率就好,最后乘上(m-1)就是得分为 i 的期望人数

    代码:

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int INF = 0x3f3f3f3f,M = 1e5+10;
    double dp[105][M],sum[M];
    int a[105];
    int n,m,s;
    double expd;
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]),s+=a[i];
    	if(m==1) {printf("%.16lf",1.0);return 0;}
    	dp[0][0]=sum[0]=1;
    	double base=1.0/(m-1);
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=min(s-1,i*m);j++)
    		{
    			int l=max(i-1,j-m),r=min(j-1,m*(i-1));
    			dp[i][j]+=base*sum[r];
    			if(l>0) dp[i][j]-=base*sum[l-1];
    			if(l<=j-a[i]&&j-a[i]<=r) dp[i][j]-=base*dp[i-1][j-a[i]];
    		}
    		for(int j=1;j<=min(s-1,i*m);j++) sum[j]=sum[j-1]+dp[i][j];
    	}
    	for(int i=1;i<s;i++) expd+=dp[n][i];
    	expd*=m-1;
    	printf("%.16lf",expd+1);
    	return 0;
    }
    

    T3 Assignment

    分段DP好题啊!

    首先,所有所需要的路径一定是总部到所有点和所有点到总部的距离,所以不用Floyd,可以用正反两次dijkstra预处理求出

    然后问题可以转化成求sigma(集合中的元素值*(集合大小-1)),由于“经验”可以发现选出的元素大小连续排布,所以需要排序,可以分段DP

    但是复杂度还是不够好,又发现选出的序列长度单调不升,第三层 k 可以减少循环次数,其中(1+1/2+1/3+...+1/n)=logn,总复杂度O(nlogn)

    代码:

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int INF = 0x3f3f3f3f,N = 5005,M = 50005;
    #define pr pair<ll,int>
    #define mp make_pair
    priority_queue<pr,vector<pr>,greater<pr> >q;
    int n,b,s,m,ecnt1,ecnt2,head1[M*2],head2[M*2],vis[N];
    ll dis[N],x[N];
    ll sum[N],dp[N][N];
    struct edge
    {
    	int to,nxt,w;
    }a1[M*2],a2[M*2];
    void add1(int x,int y,int w)
    {
    	a1[++ecnt1]=(edge){y,head1[x],w};
    	head1[x]=ecnt1;
    }
    void add2(int x,int y,int w)
    {
    	a2[++ecnt2]=(edge){y,head2[x],w};
    	head2[x]=ecnt2;
    }
    void init()
    {
    	memset(head1,-1,sizeof(head1));
    	memset(head2,-1,sizeof(head2));
    	memset(x,0,sizeof(x));
    }
    void dijk1()
    {
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	dis[b+1]=0; 
    	q.push(mp(0,b+1));
    	while(!q.empty())
    	{
    		int u=q.top().second;
    		q.pop();
    		if(vis[u]) continue;
    		vis[u]=1;
    		for(int i=head1[u];~i;i=a1[i].nxt)
    		{
    			int v=a1[i].to;
    			if(dis[v]>dis[u]+a1[i].w)
    			{
    				dis[v]=a1[i].w+dis[u];
    				q.push(mp(dis[v],v));
    			}
    		}
    	}
    }
    void dijk2()
    {
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	dis[b+1]=0; 
    	q.push(mp(0,b+1));
    	while(!q.empty())
    	{
    		int u=q.top().second;
    		q.pop();
    		if(vis[u]) continue;
    		vis[u]=1;
    		for(int i=head2[u];~i;i=a2[i].nxt)
    		{
    			int v=a2[i].to;
    			if(dis[v]>dis[u]+a2[i].w)
    			{
    				dis[v]=a2[i].w+dis[u];
    				q.push(mp(dis[v],v));
    			}
    		}
    	}
    }
    int main()
    {
    
    	while(scanf("%d%d%d%d",&n,&b,&s,&m)!=EOF)
    	{
    		init();ecnt1=0;ecnt2=0;
    	for(int i=1;i<=m;i++)
    	{
    		int u,v,w;
    		scanf("%d%d%d",&u,&v,&w);
    		add1(u,v,w);
    		add2(v,u,w);
    	}
    	dijk1();
    	for(int i=1;i<=b;i++) x[i]+=dis[i];
    	dijk2();
    	for(int i=1;i<=b;i++) x[i]+=dis[i];
    
    	sort(x+1,x+b+1);
    //	for(int i=1;i<=n;i++)
    //		printf("x[%d]=%d
    ",i,x[i]);
    	for(int i=1;i<=b;i++) sum[i]=sum[i-1]+x[i];
    	for(int i=1;i<=b;i++)
    		for(int j=1;j<=s;j++) dp[i][j]=1e16;
    	//dp[1][0]=0;
    	//dp[1][1]=x[1];
    	for(int i=1;i<=b;i++) dp[i][1]=sum[i]*(i-1);
    	for(int i=1;i<=b;i++)
    		for(int j=2;j<=s;j++)
    			for(int k=i-i/j;k<i;k++)
    			{
    				dp[i][j]=min(dp[k][j-1]+(sum[i]-sum[k])*(i-k-1),dp[i][j]);
    				//前i个元素划分成j段,[1,k],[k+1,i] 
    				//printf("dp[%d][%d]=%d
    ",i,j,dp[i][j]);
    			}
    	printf("%lld
    ",dp[b][s]);
    
    }
    	return 0;
    }
    /*
    5 4 2 10
    5 2 1
    2 5 1
    3 5 5
    4 5 0
    1 5 1
    2 3 1
    3 2 5
    2 4 5
    2 1 1
    3 4 2
    */
    
    

    T4 recoverset

    毒瘤题,现在也不太理解

  • 相关阅读:
    Linux操作系统加固建议 风行天下
    python 中 os.walk() 函数详解 风行天下
    windows系统修改文件夹颜色工具 风行天下
    linux运维python递归遍历目录+案例应用
    rsyslog写入日志文件报错的问题 风行天下
    linux存放history命令的文件 风行天下
    python 远程登陆一次执行多条命令 风行天下
    【Linux】Linux文件之/etc/fstab 风行天下
    消息队列初见:一起聊聊引入系统mq 之后的问题
    常见性能优化实践总结
  • 原文地址:https://www.cnblogs.com/conprour/p/15170929.html
Copyright © 2020-2023  润新知