dp

t1:

给一个串,问多少子序列构成的数字可以被(3)整除

(f[i][j])表示前(i)个模(3)余数为(j)的方案数

现在的+直接继承原来的方案,注意初始化

int f[N][3]; char s[N];
int main(){
	scanf("%s",s + 1);
	int n = strlen(s + 1);
	for(int i = 1;i <= n;++i)
		f[i][(s[i] - '0') % 3] = 1;
	for(int i = 1;i <= n;++i)
		for(int j = 0;j < 3;++j)
			for(int k = i+1;k <= n;++k)
				f[k][(j + s[k]-'0') % 3] = (f[k][(j+s[k]-'0')%3] + f[i][j]) % mod;
	for(int i = 1;i <= n;++i)
		f[0][0] = (f[0][0] + f[i][0]) % mod;
	printf("%d",f[0][0]);
	return 0;
}

t2:

给你一个合法的括号序列(s1),每次你可以删除一个("()",)你可以删除(0)个或者多个("()"),求能否删成另一个括号序列(s2)

(f[i][j])表示字符串前(i)个字符删除(())操作,能否匹配到(t)的前(j)个字符

(s[i]==t[j]),不用删除当前(s[i])(f[i][j]|=f[i-1][j-1])

(s[i]==)) 尝试删除(s[i])结尾())最小合法字符串序列 假设到(k) (f[i][j]~~|=~~f[k][j])

bitset<N>f[N];
int main(){
	string s,t;
	cin >> s >> t;
	int slen = s.size(),tlen = t.size();
	f[0][0] = 1;
	for(int i = 1;i <= slen;++i)
		for(int j = 1;j <= tlen;++j){
			if(s[i-1] == t[j-1]) f[i][j] = f[i][j] | f[i-1][j-1];
			if(s[i-1] == ')'){
				int k = i-1,cnt = 1;
				while(cnt > 0){
					if(s[k-1] == ')') ++cnt;
					else --cnt;
					--k;
				}
				f[i][j] = f[i][j] | f[k][j];
			}
		}
	if(f[slen][tlen]) puts("Possible");
	else puts("Impossible");
}

t3:

Jason 正在打一场 (CF)
比赛时间为(T)分钟,有(N)道题,可以在比赛时间内的任意时间提交代码
(i)道题的起始分数为(maxPoints[i]),题目的分数随着比赛的进行,每分钟减少(pointsPerMinute[i])
这是一场比较(dark)(CF),分数可能减成负数
已知第(i)道题需要花费 (requiredTime[i])的时间解决
请问最多可以得到多少分

第一行输入两个整数(N,T (1 ≤ N ≤ 50, 1 ≤ T ≤ 100000))
第二行输入(n)个整数(maxPoints[i])
第三行输入(n)个整数(pointsPerMinute[i])
第四行输入(n)个整数(requiredTime[i])
(1 ≤ maxPoints[i],pointsPerMinute[i],requiredTime[i] ≤ 100000)

1 74
502
2
47
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 55,T = 1e5 + 5;
#define int long long
int f[T];
struct node{
	int maxx;//最大分数 
	int pm;//每分钟减小的分数
	int re;//需要的时间
	bool operator < (const node &x) const{
		return pm*1.0/re > x.pm*1.0/x.re;
	}
}e[N]; int n,t;
signed main(){
//	freopen("text0.in","r",stdin);
//	freopen("text0.out","w",stdout);
	scanf("%lld%lld",&n,&t);
	for(int i = 1;i <= n;++i) scanf("%lld",&e[i].maxx);
	for(int i = 1;i <= n;++i) scanf("%lld",&e[i].pm);
	for(int i = 1;i <= n;++i) scanf("%lld",&e[i].re);
	sort(e + 1,e + n + 1); int ans = 0;
	for(int i = 1;i <= n;++i)
		for(int j = t;j >= e[i].re;--j){
			f[j] = max(f[j],f[j - e[i].re] + e[i].maxx - e[i].pm*j);
			ans = max(f[j],ans);
		}
	printf("%lld",ans);
}

t4

给你一个完全二分图,图中左右两边顶点数相同,每条边染成红绿蓝中一种,满足任意红边不共享端点,任意蓝边不共享端点,求满足条件的染色数 ((nle1e7))

可以把左右(n)个点的二分图题转化为(n imes n)的棋盘问题,题意转化成在(n imes n)棋盘放红蓝棋子,使任意行列不能有相同颜色棋子方法(但是我没转化qwq)

我们先默认所有边是绿色的,接下来,我们尝试把一些边染成红蓝色,

定义(f_i)表示二分图其中一边有(i)个点,并且边只有绿色和红色时的答案。 (f_0=1,f_1=2)

考虑如何递推,(f_{i-1} o f_i)会新增两个点

分情况考虑:

1.这对点只连绿边,方案数为(f_{i-1})

2.两点连一条红边,现在只能连绿边了,方案数为(f_{i-1})

3.这对点间连绿边,但至少一个点连了红边

这对边中的一个点,与之前的一个点之前连了一条红边,那么,其实就相当于还剩(i-1)对待连

方案数为(2 imes(i-1) imes f(i-1)) (两个点选一个,(i-1)个点对选一对)

但是我们发现,对于左边和右边连了红边的情况,我们算了两次,所以我们还要减去两边都连红边的情况,减去

((i-1)^2 imes f(i-2))

((i-1)^2) 意义为各选((i-1))对点

(f_i=2 imes i imes f(i-1)-(i-1)^2 imes f(i-2))

再考虑有蓝边的情况,注意到蓝边和红边本质相同,直接容斥即可

(ans=sumlimits_{k=0}^n(-1)^k(C_n^k)^2i!f(n-k)^2)

只填一种颜色,对于选取(i)个节点每边都有(large f_n={nchoose i}A_n^i),两边就是((f_n)^2)

t5

首先我们肯定是贪心的策略去做,就是我选了叶子节点开始染色,那么染过色的地方就尽量不要去选,这应该是最优策略。
但其实这样是有错误的
考虑这样一种情况 (4 o 3 o 2 o 1) 这是其中一条链,假设(k_1=1,k_2=1,k_3=3,k_4=2)
按照上面的做法,我们从(4)开始染色,ok,(3)(4)已经染色成功,然后继续(2)开始染色,ok,(2)染色成功,最后把根染色这样操作次数要(3)次。其实正确答案应该是(2).
(4)开始染色,(3)(4)覆盖,(3)染色,(1)(2)覆盖,结束
所以我们不仅要维护染色的最远距离,同时还需要去更新掉父亲节点的染色距离,
比如在上述例子,(k[2]=max(k[2],k[3]-1)=k[3]-1=2) 这样就可以避免上述错误了。
(dfs)维护染色最远距离,并且把子节点的染色范围更新到父亲节点去取最大值即可。
如果到当前节点无法被子树中节点的染色覆盖,那么就要将当前节点染色。

int n,ans,f[N]; vector<int>g[N]; int k[N];
void dfs(int x,int fa){
	for(int i = 0;i < g[x].size();++i){
		int v = g[x][i]; dfs(v,x);
		k[x] = max(k[x],k[v] - 1);//注意k数组,我们不关心这个最远来自谁,只要知道可以最远多少就行了
		f[x] = max(f[x],f[v] - 1);//还可以往上延申长度
	}
	if(!f[x])//如果子节点都无法覆盖到这个点
		++ans,f[x] = k[x];
}
int main(){
	n = read(); int u;
	for(int i = 2;i <= n;++i){
		u = read(); g[u].push_back(i);
	}
	for(int i = 1;i <= n;++i) k[i] = read();
	dfs(1,0); printf("%d",ans);
}
原文地址:https://www.cnblogs.com/shikeyu/p/13898413.html