20210812dp模拟赛

考的是不太擅长的DP……

赛时

题目有错误!

导致用了很长时间才看全题目……

四道题都不太会……

赛时还是写上了一些暴力,拿到了一些分数……

见赛后吧……

赛后

四道赛题,订正了很长时间。

T1

Lecture

明显是数位dp。但是考场上没写出来

与一般的dp不同,发现从最高位到最低位很难维护。

想到逆向,从最低位到最高位。

问题变得好解决:两个bool判断当前后缀是否为倍数此数是否可选取(是否均为0).

#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f , N = 1e3+5 , M = 1e2+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret = 0 ;char ch = ' ' , c = getchar();
	while(!(c >= '0' && c <= '9'))ch = c , c = getchar();
	while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
	return ch == '-' ? -ret : ret;
}
int n,k,mod;
int dp[N][M][2],Pow[N];

int dfs(int pos,int num,bool jud,bool zero){
    if(pos > n)return jud;
	if(!zero && dp[pos][num][jud] != -1)return dp[pos][num][jud];
	int ans = 0;
	for(int i = 0 ; i <= 9 ; i ++){
		if(pos == n && !i)continue;
		int nnum = (i * Pow[pos] + num) % k,
			nzero = zero && !i;
		(ans += dfs(pos+1 , nnum , jud || (!nnum && !nzero),nzero)) %= mod;
	}
	if(!zero)dp[pos][num][jud] = ans;
	return ans;
}
signed main(){
	memset(dp,-1,sizeof(dp));
	n = read() , k = read() , mod = read();
	Pow[1] = 1;
	for(int i = 2 ; i <= n ; i ++)
		Pow[i] = (Pow[i-1] * 10) % k;
	printf("%d",dfs(1,0,0,1));
}

T2

CF601C Kleofáš and the n-thlon

一个期望dp啦

dp[i][j]表示前(i)个人获得(j)分数的概率,处理期望即可。

#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f , N = 1e2+5 , M = 1e3+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret = 0 ;char ch = ' ' , c = getchar();
	while(!(c >= '0' && c <= '9'))ch = c , c = getchar();
	while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
	return ch == '-' ? -ret : ret;
}
int n,m;
int a[N],sum;
double dp[N][M*N],s[M*N];
double ans;
signed main(){
//	fo("nthlon");
	n = read() , m = read();
	for(int i = 1 ; i <= n ; i ++)
		a[i] = read() , 
		sum += a[i];
	for(int i = 1 ; i <= m ; i ++){
		if(i != a[1])
			dp[1][i] = 1.0 / (m-1);
	}
	for(int i = 2 ; i <= n ; i ++){
		for(int j = 1 ; j <= i*m ; j ++)
			s[j] = s[j-1] + dp[i-1][j];
		for(int j = i ; j <= i*m ; j ++){
			if(j <= m+1) dp[i][j] = s[j-1] - dp[i-1][j-a[i]] * (1 <= j-a[i]);
			else 		 dp[i][j] = s[j-1] - s[j-m-1] - dp[i-1][j-a[i]] * (j-m <= j-a[i]);
			dp[i][j] /= (m-1);
//			for(int k = 1 ; k <= min(j-1,m) ; k ++)
//				if(k != a[i])
//					dp[i][j] += dp[i-1][j-k]/(m-1),
//			printf("  dp[%d][%d] = s[%d][%d](%.3lf) - s[%d][%d](%.3lf)
",i,j,i-1,j-1,s[i-1][j-1],i-1,max(1,j-m)-1,s[i-1][max(1,j-m)-1]);
		}
	}
//	for(int i = 1 ; i <= n ; i ++)
//		for(int j = 1 ; j <= i*m ; j ++)
//			printf("  dp[%d][%d] = %lf
",i,j,dp[i][j]);
	for(int i = 1 ; i < sum ; i ++)
		ans += dp[n][i];
	printf("%.15lf",ans * (m-1) + 1 );
	
	return 0;
}
/*
2 2 1000
*/

T3

Branch Assignment

跑两遍dij,找出(i o b+1)(b+1 o i)的最短路。设(dis[i])为二者之和。

则接下来的任务就是将dis[1],dis[2],...dis[b]分成s 组,每组的代价为该组内dis 之和*(该组内dis 元素个数-1),目标是最小化所有组的代价之和。

便是排序之后,运用分段dp的思想进行转移。

在最优解中,dis 从小到大依次划分所得到的段的长度一定是单调不增的,则上述DP 转移中k 的最优取
值一定在([i-dfrac ij,i))之间,于是总转移复杂度就是(O(n²(1/1+1/2+1/3+...+1/n)) =O(n²logn)),足以通过本题。

#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f,N = 5e3+5 , M = 5e4+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret = 0 ;char ch = ' ' , c = getchar();
	while(!(c >= '0' && c <= '9'))ch = c , c = getchar();
	while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
	return ch == '-' ? -ret : ret;
}
int n,b,s,m;
int ecnt1 = -1 ,ecnt2 = -1 , head1[N] , head2[N];
struct Edge{int to,nxt,w;}e1[M],e2[M];
inline void add_edge1(int u,int v,int w){
	e1[++ecnt1] = (Edge){v,head1[u],w};
	head1[u] = ecnt1;
}
inline void add_edge2(int u,int v,int w){
	e2[++ecnt2] = (Edge){v,head2[u],w};
	head2[u] = ecnt2;
}
int dis1[N],dis2[N];ll dis[N];bool vis1[N],vis2[N];
typedef pair<int,int> pr;
priority_queue<pr,vector<pr>,greater<pr> >q; 
void dij1(int s){
	while(!q.empty())q.pop();
	memset(dis1,0x3f,sizeof(dis1));
	memset(vis1,0,sizeof(vis1));
	dis1[s] = 0 ; q.push(make_pair(0,s));
	while(!q.empty()){
		int u = q.top().second;q.pop();
		if(vis1[u])continue;
		vis1[u] = 1;
		for(int i = head1[u] ; ~i ; i = e1[i].nxt){
			int v = e1[i].to , w = e1[i].w;
			if(dis1[v] > dis1[u] + e1[i].w)
				dis1[v] = dis1[u] + e1[i].w,
				q.push(make_pair(dis1[v],v));
		}
	}
}
void dij2(int s){
	while(!q.empty())q.pop();
	memset(dis2,0x3f,sizeof(dis2));
	memset(vis2,0,sizeof(vis2));
	dis2[s] = 0 ; q.push(make_pair(0,s));
	while(!q.empty()){
		int u = q.top().second;q.pop();
		if(vis2[u])continue;
		vis2[u] = 1;
		for(int i = head2[u] ; ~i ; i = e2[i].nxt){
			int v = e2[i].to , w = e2[i].w;
			if(dis2[v] > dis2[u] + e2[i].w)
				dis2[v] = dis2[u] + e2[i].w,
				q.push(make_pair(dis2[v],v));
		}
	}
}
ll dp[N][N],sum[N];
void work(){
//	fo("assignment");
	memset(head1,-1,sizeof(head1));memset(head2,-1,sizeof(head2));
	ecnt1 = ecnt2 = -1;
	for(int i = 1 ; i <= m ; i ++){
		int u = read() , v = read() , w = read();
		add_edge1(u,v,w);
		add_edge2(v,u,w);
	}
	dij1(b+1);dij2(b+1);
	for(int i = 1 ; i <= b ; i ++)
//		printf("%d : %d,%d
",i,dis1[i],dis2[i]),
		dis[i] = dis1[i] + dis2[i];
	sort(dis+1,dis+b+1);
	for(int i = 1 ; i <= b ; i ++)
		sum[i] = sum[i-1] + dis[i];
//	memset(dp,0x3f,sizeof(dp));
	dp[0][0] = 0;
	for(int i = 1 ; i <= b ; i ++){
		dp[i][0]= 1LL * INF * INF;
		for(int j = 1 ; j <= min(i,s) ; j ++){
			dp[i][j] = 2e15;
			for(int k = i-i/j ; k <= i ; k ++)
			dp[i][j] = min(dp[i][j],dp[k][j-1] + (sum[i]-sum[k]) * (i-k-1));
//			printf("  dp[%d][%d] = min(dp[%d][%d](%lld) + sum[%d,%d](%lld) * (%d-%d-1)))
",i,j,k,j-1,dp[k][j-1],i,k,sum[i]-sum[k],i,k);
		}
	} 
//	for(int i = 1 ; i <= b ; i ++)
//		for(int j = 1 ; j <= min(i,s) ; j ++)
//			printf("dp[%d][%d] = %lld
",i,j,dp[i][j]);
	printf("%lld
",dp[b][s]);
}
signed main(){
	while(scanf("%d %d %d %d",&n,&b,&s,&m) != EOF)
		work();
	return 0;
}
原文地址:https://www.cnblogs.com/Shinomiya/p/15135604.html