NOI2019同步赛 day1

本文针对三道算法竞赛题目提供了详细的解答思路与代码实现,包括一个时间复杂度玄学的DP问题、一个暴力求解的问题以及一个O(n^4)的水DP问题。

T1

回家路线==归程?

题面又臭又长。。

先瞅几眼数据范围,A=B=C的部分分是什么玩意?
最早到达时间?
A=B=0似乎可以Dp?

后来想到一种时间复杂度玄学的DP
d p [ u ] [ t ] 表 示 在 时 间 t 到 达 u 的 最 小 代 价 dp[u][t]表示在时间t到达u的最小代价 dp[u][t]tu
转移方程: d p [ u ] [ t ] = d p [ v ] [ q [ i ] ] + F ( p [ i ] − t ) dp[u][t]=dp[v][q[i]]+F(p[i]-t) dp[u][t]=dp[v][q[i]]+F(p[i]t)
边界: d p [ u ] [ t ] = t dp[u][t]=t dp[u][t]=t
70%范围内跑的飞快

Code:

#include <bits/stdc++.h>
#define maxn 2010
#define maxt 1010
#define maxm 200010
#define LL long long
using namespace std;
const LL inf = 1e12;
struct Edge{
	int to, next, p, q;
}edge[maxm];
int num, head[maxn], n, m, A, B, C; 
LL dp[maxn][maxt];

inline int read(){
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

void addedge(int x, int y, int p, int q){ edge[++num] = (Edge) { y, head[x], p, q }; head[x] = num; }
LL F(int x){ return 1LL * A * x * x + B * x + C; }

void dfs(int u, int t){
	if (u == n) dp[u][t] = t;
	if (dp[u][t] < inf) return;
	for (int i = head[u]; i; i = edge[i].next){
		int v = edge[i].to; 
		if (edge[i].p >= t){
			dfs(v, edge[i].q);
			dp[u][t] = min(dp[u][t], dp[v][edge[i].q] + F(edge[i].p - t));
		}
	}
}

int main(){
	freopen("route.in", "r", stdin);
	freopen("route.out", "w", stdout);
	n = read(), m = read(), A = read(), B = read(), C = read();
	for (int i = 1; i <= m; ++i){
		int x = read(), y = read(), p = read(), q = read();
		addedge(x, y, p, q);
		dp[y][q] = inf;
	}
	dp[1][0] = inf;
/*	for (int i = 1; i <= n; ++i)
		for (int j = 0; j <= 1000; ++j) dp[i][j] = inf;*/
	dfs(1, 0);
	printf("%lld\n", dp[1][0]);
	return 0;
}

T2

20%暴力随便打好
希望找一下规律失败
这题没怎么思考,就这样了

Code:

#include <bits/stdc++.h>
#define maxn 310
#define qy 1000000007
using namespace std;
int a[maxn], b[maxn], ans, n, num[maxn];

inline int read(){
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

void Do(){
	for (int s = 1; s <= n; ++s){
		int x = 0, i = s - 1;
		while (i >= 1 && num[i] <= num[s]) ++x, --i;
		int y = 0, j = s + 1;
		while (j <= n && num[j] < num[s]) ++y, ++j;
		if (abs(x - y) > 2) return;
	}
	(++ans) %= qy;
}

void dfs(int k){
	if (k > n) Do(); else{
		for (int i = a[k]; i <= b[k]; ++i){
			num[k] = i;
			dfs(k + 1);
		}
	}
}

int main(){
	freopen("robot.in", "r", stdin);
	freopen("robot.out", "w", stdout);
	n = read();
	for (int i = 1; i <= n; ++i) a[i] = read(), b[i] = read();
	dfs(1);
	printf("%d\n", ans);
	return 0; 
}

T3

马上设计出了28分 O ( n 4 ) 的 水 d p O(n^4)的水dp O(n4)dp
d p [ i ] [ s l ] [ k 1 ] [ k 2 ] 表 示 第 i 位 , 总 共 s l 位 下 标 相 同 , 各 取 了 k 1 , k 2 个 dp[i][sl][k1][k2]表示第i位,总共sl位下标相同,各取了k1,k2个 dp[i][sl][k1][k2]islk1,k2
转移方程: d p [ i ] [ s l ] [ k 1 ] [ k 2 ] = m a x ( d p [ i − 1 ] [ s l − 1 ] [ k 1 − 1 ] [ k 2 − 1 ] + a [ i ] + b [ i ] , d p [ i − 1 ] [ s l ] [ k 1 − 1 ] [ k 2 ] + a [ i ] , d p [ i − 1 ] [ s l ] [ k 1 ] [ k 2 − 1 ] + b [ i ] , d p [ i − 1 ] [ s l ] [ k 1 ] [ k 2 ] ) dp[i][sl][k1][k2]=max(dp[i-1][sl-1][k1-1][k2-1]+a[i]+b[i],dp[i-1][sl][k1-1][k2]+a[i],dp[i-1][sl][k1][k2-1]+b[i],dp[i-1][sl][k1][k2]) dp[i][sl][k1][k2]=max(dp[i1][sl1][k11][k21]+a[i]+b[i],dp[i1][sl][k11][k2]+a[i],dp[i1][sl][k1][k21]+b[i],dp[i1][sl][k1][k2])
A N S = m a x ( d p [ n ] [ i ] [ k ] [ k ] ) ( l &lt; = i &lt; = k ) ANS=max(dp[n][i][k][k])(l&lt;=i&lt;=k) ANS=max(dp[n][i][k][k])(l<=i<=k)

Code:

#include <bits/stdc++.h>
#define LL long long
#define maxn 32
using namespace std;
int n, K, L;
LL dp[2][maxn][maxn][maxn], a[maxn], b[maxn];

inline LL read(){
	LL s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

int main(){
	freopen("sequence.in", "r", stdin);
	freopen("sequence.out", "w", stdout);
	int M = read();
	while (M--){
		n = read(), K = read(), L = read();
		for (int i = 1; i <= n; ++i) a[i] = read();
		for (int i = 1; i <= n; ++i) b[i] = read();
		memset(dp, 0, sizeof(dp));
		for (int i = 1; i <= n; ++i){
			int p = i & 1;
			for (int j = 0; j <= K; ++j)	
				for (int k = j; k <= K; ++k)
					for (int l = j; l <= K; ++l){
						dp[p][j][k][l] = dp[p ^ 1][j][k][l];
						if (j && k && l) dp[p][j][k][l] = max(dp[p][j][k][l], dp[p ^ 1][j - 1][k - 1][l - 1] + a[i] + b[i]);
						if (k) dp[p][j][k][l] = max(dp[p][j][k][l], dp[p ^ 1][j][k - 1][l] + a[i]);
						if (l) dp[p][j][k][l] = max(dp[p][j][k][l], dp[p ^ 1][j][k][l - 1] + b[i]);
					}
		}
		int p = n & 1;
		LL ans = 0; 
		for (int i = L; i <= K; ++i) ans = max(ans, dp[p][i][K][K]); 
		printf("%lld\n", ans);
	}
	return 0;
}

也不知道什么时候公布成绩。。。
期望得分70+20+28=118

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值