首页 > 其他分享 >师妹的整理

师妹的整理

时间:2023-05-12 14:44:59浏览次数:27  
标签:std 师妹 int ll pos ans 整理 dp

Digital DP

template

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[20];
ll dp[20][state];
ll dfs(int pos, /*state变量*/, bool lead/*前导零*/, bool limit/*数位上界变量*/)
{
    if(pos == -1) return 1;
    if(!limit && !lead && dp[pos][state] != -1) return dp[pos][state];
    int up = limit ? a[pos] : 9;
    ll ans = 0;
    for(int i = 0; i <= up; i++)
    {
        if() ...
        else if()...
        ans += dfs(pos - 1, /*状态转移*/, lead && i == 0, limit && i == a[pos]); 
    }  
    if(!limit && !lead) dp[pos][state] = ans;
    return ans;
}
ll solve(ll x)
{
    int pos = 0;
    while(x)
    {
        a[pos++] = x % 10;
        x /= 10;
    }
    return dfs(pos - 1/*从最高位开始枚举*/, /*一系列状态 */, true, true);
}
int main()
{
    ll le, ri;
    memset(dp, -1, sizeof(dp));
    while(~scanf("%lld%lld", &le, &ri))
    {
        printf("%lld\n", solve(ri) - solve(le - 1));
    }
}
<div STYLE="page-break-after: always;"></div>

Digit sum

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[20000], digit;
ll dp[2000][2000];
ll ans[10], ans2[10];
ll dfs(int pos, /*state变量*/int cnt, bool lead/*前导零*/, bool limit/*数位上界变量*/)
{
    if(pos == -1)
     return cnt;
    if(!limit && !lead && dp[pos][cnt] != -1) return dp[pos][cnt];
    int up = limit ? a[pos] : 9;
    ll ans = 0;
    for(int i = 0; i <= up; i++)
    {
        int ncnt = cnt + (i == digit);
        if(lead && i == 0 && digit == 0)
            ncnt = 0;
        ans += dfs(pos - 1, /*状态转移*/ncnt, lead && i == 0, limit && i == a[pos]); 
    }
    if(!limit && !lead) dp[pos][cnt] = ans;
    return ans;
}
void solve(ll x)
{
    int pos = 0;
    while(x)
    {
        a[pos++] = x % 10;
        x /= 10;
    }
    for(int i = 0; i <= 9; i++)
    {
        digit = i;
        ans[i] += dfs(pos - 1/*从最高位开始枚举*/, 0/*一系列状态 */, true, true);
    }
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    ll le, ri;
    memset(dp, -1, sizeof(dp));
    while(cin>>le>>ri)
    {
        if(!le && !ri)  break;
        if(le > ri)
            swap(le, ri);
        memset(ans, 0, sizeof(ans));
        solve(ri);
        memcpy(ans2, ans, sizeof(ans));
        memset(ans, 0, sizeof(ans));
        solve(le - 1);
        for(int i = 0; i <= 9; i++)
        {
            cout<<ans2[i] - ans[i];
            if(i != 9)
                cout<<" ";
        }
        cout<<endl;
    }
}
<div STYLE="page-break-after: always;"></div>

Non-falling number

/*
不降数的个数应该与位数以及最高位数字有关
状态:f[i][j]表示一共有i位,且最高位为j的不降数的个数
	j K X X ... X
	i i-1
转移:因为最高位已经固定为j,所以假定第i-1位是k,根据不降数
定义k>=j,所以f[i][j] = Σ(k=j~9)f[i-1][k]
即f[i][j] = f[i-1][j+1]+f[i-1][j+2]+...+f[i-1][9]

因为是从高位往低位算,所以我们需要做预处理
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 12;
int a[N];//把整数每一位抠出来
int f[N][N];//f[i][j]表示一共i位,且最高位是j的不降数的个数

void init()
{
	for(int i = 0;i <= 9; i++)	f[1][i] = 1;//一位数
	for(int i = 2; i < N; i++)//阶段:枚举位数
		for(int j = 0; j <= 9; j++)//状态:枚举最高位
			for(int k = j; k <= 9; k++)//决策:枚举次高位
				f[i][j] += f[i - 1][k];
}

int calc(int n)
{
	if(!n)//特判,n=0直接返回1
		return 1;
	int cnt = 0;
	while(n)
		a[++cnt] = n % 10,n /= 10;
	int res = 0, last = 0;//last表示上一位数字
	for(int i = cnt; i; i--)
	{
		int now = a[i];//表示当前位
		for(int j = last; j < now; j++)//枚举当前位可填入的数字
			res += f[i][j];
		if(now < last)break;//若小break
		last = now;
		if(i == 1)	res++;//特判,走到了a1(原数)
	}
	return res;
}

int main()
{
	init();
	int l, r;
	while(cin>>l>>r)
	{
		cout<<calc(r) - calc(l - 1)<<endl;
	}
	return 0;
}
<div STYLE="page-break-after: always;"></div>

P1836 Number of pages

//求1-n所有数字单个数字的和

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[20];
ll dp[200][200];
ll dfs(int pos, /*state变量*/ll sum, bool lead/*前导零*/, bool limit/*数位上界变量*/)
{
    if(pos == -1) return sum;
    if(!limit && !lead && dp[pos][sum] != -1) return dp[pos][sum];
    int up = limit ? a[pos] : 9;
    ll ans = 0;
    for(int i = 0; i <= up; i++)
    {
        ll tmp = sum + i;
        if(lead && i == 0)
            tmp = 0;
        ans += dfs(pos - 1, tmp, lead && i == 0, limit && i == a[pos]); 
    }  
    if(!limit && !lead) dp[pos][sum] = ans;
    return ans;
}
ll solve(ll x)
{
    int pos = 0;
    while(x)
    {
        a[pos++] = x%10;
        x /= 10;
    }
    return dfs(pos - 1/*从最高位开始枚举*/, 0/*一系列状态 */, true, true);
}
int main()
{
    memset(dp, -1, sizeof(dp));
    ll n;
    cin>>n;
    cout<<solve(n)<<endl;
}
<div STYLE="page-break-after: always;"></div>

P2602 [ZJOI2010] Digital count

//0-9 在 [a,b]中出现了多少次。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[20];
ll dp[20][20][2], digit;
ll ans1[20], ans2[20];
ll dfs(int pos, bool lead, bool limit, int cnt)
{
    if(pos==-1) return cnt;
    if(!limit && !lead && dp[pos][cnt][digit != 0] != -1) return dp[pos][cnt][digit != 0];
    int up = limit ? a[pos] : 9;
    ll ans = 0;
    for(int i = 0; i <= up; i++)
    {
        int tmp = cnt + (i == digit);
        if(lead && digit == 0 && i==0)  tmp = 0;
        ans += dfs(pos - 1, lead && i == 0, limit && i == a[pos], tmp); 
    }
    if(!limit && !lead) dp[pos][cnt][digit != 0] = ans;
    return ans;
}
void solve(ll x)
{
    int pos = 0;
    while(x)
    {
        a[pos++] = x % 10;
        x /= 10;
    }
    for(int i = 0; i <= 9; i++)
    {
        digit = i;
        ans1[i] += dfs(pos - 1, true, true, 0);
        //cout<<ans1[i]<<" ";
    }
}
int main()
{
    ll le, ri;
    memset(dp, -1, sizeof(dp));
    while(~scanf("%lld%lld", &le, &ri))
    {
        solve(ri);
        memcpy(ans2, ans1, sizeof(ans1));
        memset(ans1, 0, sizeof(ans1));
        solve(le - 1);
        for(int i = 0; i <= 9; i++)
            cout<<ans2[i] - ans1[i]<<" ";
        cout<<endl;
    }
}
<div STYLE="page-break-after: always;"></div>

P2657 [SCOI2009] windy num

/*
不含前导零且相邻两个数字之差至少为 2 的正整数被称为 windy 数。windy 想知道,
在 a和 b 之间,包括 a 和 b ,总共有多少个 windy 数?
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[20];
ll dp[20][20];//不同题目状态不同
ll dfs(int pos, /*state变量*/int pre, bool lead/*前导零*/, bool limit/*数位上界变量*/)//不是每个题都要判断前导零
{
    if(pos == -1)   return 1;
    if(!limit && !lead && dp[pos][pre] != -1) return dp[pos][pre];
    int up = limit ? a[pos] : 9;
    ll ans = 0;
    for(int i = 0; i <= up; i++)
    {
        int delta = abs(pre - i);
        if(delta < 2&& !lead)   continue;
        // if(i==up&&limit)ans+=dfs(pos-1,i,false,true);
        // else ans+=(i||!lead)?dfs(pos-1,i,false,false):dfs(pos-1,i,true,false);
        ans += dfs(pos-1, i,(lead && !i), limit && i == a[pos]);
    }
    if(!limit && !lead) dp[pos][pre] = ans;
    return ans;
}
ll solve(ll x)
{
    int pos = 0;
    while(x)
    {
        a[pos++] = x % 10;
        x /= 10;
    }
    return dfs(pos - 1, 0, true, true);
}
int main()
{
    ll le, ri;
    memset(dp, -1, sizeof(dp));
    while(~scanf("%lld%lld", &le, &ri))
    {
        printf("%lld\n", solve(ri) - solve(le - 1));
    }
}
<div STYLE="page-break-after: always;"></div>

P4124 [CQOI2016] Mobile phone number

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[20];
ll dp[20][20][20][4][2][2];
//第i为,上一位,上上一位,是否3个连续,是否有4,是否有8
ll dfs(int pos, int pre, int ppre, bool state, bool _4, bool _8, bool limit/*数位上界变量*/)
{
    if(_4 && _8)    return 0;
    if(pos == -1)   return state;
    if(!limit && dp[pos][pre][ppre][state][_4][_8] != -1)   return dp[pos][pre][ppre][state][_4][_8];
    int up = limit ? a[pos] : 9;
    ll ans = 0;
    for(int i = 0; i <= up; i++)
    {
        ans += dfs(pos - 1, i, pre, state || (i == pre && i == ppre), (_4 || (i == 4)), _8 || (i == 8), limit && (i == a[pos])); 
    }
    if(!limit) dp[pos][pre][ppre][state][_4][_8] = ans;
    return ans;
}
ll solve(ll x)
{
    int pos = 0;
    while(x)
    {
        a[pos++] = x % 10;
        x /= 10;
    }
    if(pos!=11)return 0;
    ll ans = 0;
   for(int i = 1; i <= a[pos - 1]; i++)
   {
        ans += (long long)dfs(pos - 2, i, 0, 0, i == 4, i == 8, i == a[pos - 1]);
   }
   return ans;
}
int main()
{
    ll le, ri;
    memset(dp, -1, sizeof(dp));
    while(~scanf("%lld%lld", &le, &ri))
    {
        printf("%lld\n", solve(ri) - solve(le - 1));
    }
}
//10000000000 53628881996
//2186241614
<div STYLE="page-break-after: always;"></div>

P6218 [USACO06NOV] Round Numbers S

//圆数:二进制0的数目不小于1,区间 [l,r][l,r] 中有多少个「圆数」
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[33];
ll dp[33][66];
ll dfs(int pos, int state, bool lead, bool limit)
{
    if(pos == -1)   return state >= 32;
    if(!limit && !lead && dp[pos][state] != -1)     return dp[pos][state];
    int up = limit ? a[pos] : 1;
    ll ans = 0;
    for(int i = 0; i <= up; i++)
    {
        if(lead && i == 0)  ans += dfs(pos - 1, state, lead, limit && i == a[pos]);
        else    ans += dfs(pos - 1, state + (i == 0 ? 1 : -1), lead && i == 0, limit && i == a[pos]);
    }
    if(!limit && !lead) dp[pos][state] = ans;
    return ans;
}
ll solve(ll x)
{
    int pos = 0;
    while(x)
    {
       a[pos++] = x & 1;
       x >>= 1;
    }
    return dfs(pos - 1/*从最高位开始枚举*/, 32/*一系列状态 */, true, true);
}
int main()
{
    ll le, ri;
    memset(dp, -1, sizeof(dp));
    while(~scanf("%lld%lld", &le, &ri))
    {
        printf("%lld\n", solve(ri) - solve(le - 1));
    }
}
<div STYLE="page-break-after: always;"></div>

Interval DP

template

/*
核心代码
memset(dp, 0, sizeof(dp))           //初始dp数组
for(int len = 2; len <= n; len++){  //枚举区间长度
    for(int i = 1; i < n; ++i){     //枚举区间的起点
        int j = i + len - 1;        //根据起点和长度得出终点
        if(j > n) break;            //符合条件的终点
        for(int k = i; k <= j; ++k) //枚举最优分割点
            dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + w[i][j]);
                                    //状态转移方程
    }
}
*/
#include<iostream>
using namespace std;
const int  N = 310;
int s[N], f[N][N];
int n;
int main()
{
    cin>>n;
    for(int i = 1; i <= n; i++)
    {
        cin>>s[i];
        s[i] += s[i - 1];
    }
    for(int len = 2; len <= n; len++)
    {
        for(int i = 1; i + len - 1 <= n; i++)
        {
            int l = i, r = i + len - 1;
            f[l][r] = 1e8;
            for(int k = l; k < r; k++)
            {
                f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
            }
        }
    }
    cout<<f[1][n];
}
<div STYLE="page-break-after: always;"></div>

ICPC Beijing 2017 J, Pangu and Stones

/*
有n堆石子,每堆有ai个,给定L,R,相邻的连续[L,R]堆石子可以合并成一堆,
代价是这些堆中石子数量之和

求把n堆石子合并成一堆的最小代价,情况不存在输出0
g[l][r][k] l~r合并成k堆的最小代价和
k>=2 g[l][r][k] = min(g[l,m,1]+g[m+1,r,k-1]); 枚举分界点改用dp去做
k=1   min(g[l,r,k]+石子数量和)


思路:
可以把合并所有石头的过程拆分成几个子步骤,首先合并连续的一些,然后再合并连续的一些,大区间的结果可以由小区间推出,所以就从小区间开始考虑,逐步推向大区间,可以用 dp[i][j][k] 表示区间 [i,j] 分成 k 堆得最小代价,对于固定的一个区间,肯定是取所有情况的最小值,最后答案是 dp[1][n][1] ,
注意边界处理,包括刚开始的初始化。
然后就是代码处理中的一些细节了。

首先将所有的 f[i][j][k] 置为 INF 。

然后对于所有的初始状态(即 f[i][j][j-i+1] )都置为 0 。
因为区间 [i,j] 内本身就有 j−i+1 个元素,所以这些元素本身就这么多堆,是不需要花费代价去划分的。这就是我所说的初始状态。

然后就是合并区间了,区间合并一般都是小区间开始合并到大区间,我们这里也不例外(记忆化搜索的话就得反着来了)。
对于一个区间 [l,r] ,它要么是直接合并成一堆,要么是若干个区间拼接到一起。所以我们这里分情况讨论:
直接合并
如果区间 [l,r] 直接合并,那么合并成一堆的最小代价应该是 f[l][r][1] 。
那么对于区间 [l,r] ,我一定可以将其拆分成两个区间 [l,i] 和 [i+1,r] ,其中左区间有 j 个元素,右区间有 1 个元素,并且满足 L≤j+1≤R 。
于是我们可以从 l~r−1 枚举 i ,从 L−1~R−1 枚举 j ,
则 f[l][r][1]=min(f[l][i][j]+f[i+1][r][1])+sum[r]−sum[l−1] 。(这里 sum[r]−sum[l−1] 表示区间 [l,r] 范围内的石子数量之和)

区间拼接
这里讲的区间拼接其实就是对于两个区间 [l,j] 和 [j+1,r] ,假设他们分别有 i−1 堆和 1 堆石子,那么这个区间总共有 i 堆石子。
区间拼接就不需要考虑合并了。
所以对于区间 [l,r] 包含 i 堆石子的情况,它对应状态 f[l][r][i] ,那么它总能拆分成两个状态(f[l][j][i−1] 和 f[j+1][r][1] ,其中 l≤j<r)的拼接。
可以推导出状态转移方程为: f[l][r][i]=min(f[l][j][i−1]+f[j+1][r][1]) ,其中 2≤i≤len,l≤j<r 。
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
const int inf = 1 << 29;
int n, L, R, sum[N], a[N], f[N][N][N];
int main()
{
	while(cin>>n>>L>>R)
	{
		for(int i = 1; i <= n; i++)
		{
			cin>>a[i];
			sum[i] = sum[i - 1] + a[i];
		}

		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= n; j++)
				for(int k = 1; k <= n; k++)
					f[i][j][k] = inf;

		for(int l = 1; l <= n; l++)
			for(int r = l; r <= n; r++)
				f[l][r][r - l + 1] = 0;

		for(int len = 1; len <= n; len++)
		{
			for(int l = 1; l + len -1 <= n; l++)
			{
				int r = l + len - 1;
				
				for(int i = l; i < r; i++)//直接合并
				{
					for(int j = L - 1; j < R; j++)//左区间j个元素,右区间1个元素
					{
						f[l][r][1] = min(f[l][r][1], f[l][i][j] + f[i + 1][r][1] + sum[r] - sum[l - 1]);
					}
				}

				for(int i = 2; i < len; i++)//区间拼接 ,设该区间总共i堆石子
				{
					for(int j = l; j < r; j++)
					{
						f[l][r][i] = min(f[l][r][i], f[l][j][i - 1] + f[j + 1][r][1]);
					}
				}
			}
		}
		if(f[1][n][1] == inf)	cout<<0<<endl;
		else cout<<f[1][n][1]<<endl;
	}
	return 0;
}
<div STYLE="page-break-after: always;"></div>

Parenthesis sequence

#include<bits/stdc++.h>
using namespace std;
int f[510][510];
int n;
char str[510];
//f[i][j]表示si,si+1...sj种最长的合法子序列长度
/*
转移:
1.f[i][j] = max(f[i][j],f[i+1][j])
2.f[i][j] = max(f[i][j],f[i][j-1])
3.(A),[A]:如果si和sj匹配,f[i][j]=max(f[i][j],f[i+1][j-1]+2)
4.AB:枚举AB的分界线k,f[i][j]=max(f[i][j],f[i][k]+f[k+1][j])
前面一个合法序列A和后面一个合法序列B

其实1,2不用考虑,为什么呢?
因为4枚举分界点,包含了1,2
*/
int main()
{
	cin>>n>>(str + 1);
	memset(f, 0, sizeof(f));
	for(int i = 1; i < n; i++)//枚举区间长度
	{
		for(int j = 1; j <= n - i; j++)//左端点
		{
			if((str[j] == '(' && str[j + i] ==')') || (str[j] == '[' && str[j + i] == ']'))
				f[j][j + i] = f[j + 1][j + i - 1] + 2;
			for(int k = j; k < j + i; k++)
			{
				f[j][j + i] = max(f[j][j + i],f[j][k] + f[k + 1][j + i]);
			}
		}
	}
	cout<<f[1][n]<<endl;
	return 0;
}

/*
10
]]][()]])[

4
*/
<div STYLE="page-break-after: always;"></div>

Stone merging

//石子合并Ⅱ
#include<bits/stdc++.h>
using namespace std;
/*
圆上一共有n条边,每次合并都要用掉一条边
最后一定有一条边没用,我们枚举哪条边没用
剩下又变成链上问题了
时间复杂度O(n^4)

能否优化?
考虑一条长度为2n的链,是由两个a数组接起来的,
也就是说,>n的位置i对应了原来的第i-n堆石子
时间复杂度O(n^3)
*/
int n, a[501 * 2], f[501][501], s[501];
int main()
{
	cin>>n;
	for(int i = 1; i <= n; i++)
	{
		cin>>a[i];
		a[i + n] = a[i];
	}
	memset(f, 127, sizeof(f));
	n = n * 2;
	for(int i = 1; i <= n; i++)
	{
		s[i] = s[i - 1] + a[i];
	}
	for(int i = 1; i <= n; i++)	f[i][i] = 0;
	for(int i = 1; i < n; i++)
	{
		for(int j = 1; j <= n - i; j++)
		{
			for(int k = j; k < j + i; k++)
			{
				f[j][j + i] = min(f[j][j + i], f[j][k] + f[k + 1][j + i] + s[j + i] - s[j - 1]);
			}
		}
	}
	int ans = 1 << 30;
	for(int i = 1; i <= n / 2; i++)
	{
		ans = min(ans, f[i][i + n / 2 - 1]);
	}
	cout<<ans<<"\n";
	return 0;
}

/*
abcabcabc
abcabc
*/
<div STYLE="page-break-after: always;"></div>

Probability DP

summary

——————————————小小总结QAQ——————————————

有这样一类问题,对于任意状态A,已知
①状态A所有后继状态
②设从状态A转移到后继状态B的概率P(A,B),则ΣB是A的后继状态P(A,B)=1
③从状态A转移到后继状态B的花费是W(A,B)
求解:从起始状态S到终止状态T的期望花费

求解这类问题的基本模式是:设E(A)表示从状态A到终止状态T的期望花费
初值:E(T) = 0;
那么可以得到转移公式:
E(A) = Σ(Bi是A的后继状态)P(A,B)*(E(Bi)+W(A,Bi))
我们可以把通过倒推(终点到起点,推出起点期望)
当转移关系不成环时,状态转移是线性的,可按拓扑排序递推求解
当转移关系成环时,列出所有状态转移方程,使用高斯消元解决

eg1.
给定一个起点是1,终点为n的有向无环图。
到达每一个顶点时,如果有k条离开该点的道路,可以选择任意一条道路离开该点
并且走向每条路的概率为1/k。
问你从1出发走到n的路径期望总长度是多少

dp[i] = Σ(j是i的后继状态)(dp[j]+w[i][j])*(1/k)
这题的终点很明确,那就是走到n即停止,对于期望DP,我们
一般采用逆序的方式来定义,即考虑当前状态到终点的期望代价
dp[i]为从i出发走到终点n的路径期望总长度

eg2.
有n个人排成一列,每一秒队伍最前面的人有p的概率走上电梯(一旦走上电梯就会一直在电梯上)
或者有1-p的概率不动,问T秒后,电梯上的人数期望

dp[i][j]:前i秒,电梯上有j个人的概率
dp[0][0] = 1;
dp[i+1][j+1] = dp[i][j]p;
dp[i+1][j] = dp[i][j]
(1-p);

eg3.
给定一个序列,一些位置未确定(是o与x的几率各占50%),对于一个ox序列,
连续a长度的o会得到a^2的收益,请问最终得到的序列的期望收益的多少?

观察到(a+1)2-a2 = 2a+1
根据期望的线性性质,我们可以分别计算出每一个字符的贡献
设l(i)表示以i结尾的极大连续o的长度的期望
设第i个字符为s,那么有三种情况:

ooo?o

l[4] = 1/24+1/20 = 2
1.s = o,对答案的贡献l[i-1]2+1,l[i] = l[i-1]+1;
2.s = x,对答案贡献0,l[i] = 0;
3.s = ?,对答案贡献(l[i-1]
2+1)/2,l[i] = (l[i-1]+1)/2

对于每个字符对答案的贡献是一个长度为a的一次多项式(2a+1)
对于期望的性质可知E(2a+1) = 2E(a)+1

所以计算连续o的长度的期望,即可求得序列的期望收益

eg3'.
给定一个序列,每个位置为o的几率为pi,为x的几率为1-pi,对于一个ox序列
连续a长度的o会得到a^3的收益,问最终得到的ox序列的期望收益是多少?
(a+1)^3 - a^3 = 3a^2+3a+1
我们分别计算每一个字符的贡献,注意E(a2)≠E(a)2,所以我们还有计算
以i为几位的极大连续o的长度的平方的期望
定义l1(i)表示:以i为结尾的极大连续o的长度的期望
定义l2(i)表示:以i为结尾的极大连续o的长度的平方的期望
计算l1和l2即可得到序列期望收益
设第i个字符为s,那么有三种情况:
1.s = o,对答案的贡献3l2(i-1)+3l1(i-1)+1,l[i] = l[i-1]+1
2.s = x,对答案贡献0,l[i] = 0;
3.s = ?,对答案贡献(3l2(i-1)+3l1(i-1)+1)/2,l[i] = (l[i-1]+1)/2

总结:
计算期望有几种基本的思路:
①计算从当前状态到终止状态的期望。将每个状态的期望值写成状态转移式,
终止状态的期望值往往是已知的
②利用期望的线性性质:E(x+y) = E(x)+E(y)。计算部分对整体的贡献。
最后两个例题就是根据期望的线性性质来计算答案的例子.当状态数量过大或难以表示时,就考虑分离变量的方法
③算出每个状态的出现概率,进而算出每个状态对答案的期望贡献
列出状态的概率转移式,起始状态的出现概率为1.
若状态是无环的则利用刷表法正向地推出每个状态的出现概率;
若有环,则列出所有状态的转移方程,将概率看作未知量,解线性方程即可

<div STYLE="page-break-after: always;"></div>

P4550 Collect stamps

//倒推+第n天完成的期望次数

#include<bits/stdc++.h>
using namespace std;
const int N = 10010;
double num[N], ans[N];
int main()
{
	int n;
	cin>>n;
	num[n] = 0, ans[n] = 0;
	for(int i = n - 1; i >= 0; i--) 
	{

		//num[i] = (double)(num[i]+1)*(i*(1.0)/n)+(num[i+1]+1)*((n-i)*(1.0)/n);
		num[i] = (((num[i + 1]) * ((n - i) * (1.0) / n)) + 1) / (1 - (i * (1.0) / n));
	}
	for(int i = n - 1; i >= 0; i--)
	{
		//ans[i] = (double)(ans[i]+num[i]+1)*((1.0)*i/n)+(ans[i+1]+num[i+1]+1)*((1.0)*(n-i)/n);
		ans[i] = (ans[i + 1] * (n - i) * (1.0) / n + num[i] * (i * 1.0) / n + num[i + 1] * (n - i) * (1.0) / n + 1)/(1 - i * 1.0 / n);
	}
	printf("%.2f\n",ans[0]);
	return 0;
}
<div STYLE="page-break-after: always;"></div>

walk

/*
题意:
蜗蜗的世界里有 n 个城市,城市之间通过 m 条单向高速公路连接,
初始他在 1 号城市。蜗蜗想去 n 号城市游玩,假设现在他在 x 号城市,
他会等概率地选择从 x 出发的高速公路中的一条走过去。如果没有任何从 x 号城里出发的高速公路,他就只能留在原地了。
蜗蜗会一直走直到他无路可走。
请问蜗蜗有多大的概率能够走到 n 号城市。

每行两个整数 x,y(1≤x<y≤n) 描述一条从 x 号城市到 y 号城市的高速公路。

输出:
相对误差或绝对误差在eps = 1e-6 内即为正确。
关于相对误差和绝对误差:
绝对误差:你求的是a,正确答案是b,那绝对误差|b-a|
相对误差:|b-a|/max(1,a)(误差占答案的百分比)

思路:
用f[x]表示能走到x号城市的概率,f[1]=1;
考虑从x号城市出发的到y号城市的高速公路,通过下号城市走到y号城市的概率有多大呢?
	f[y]+=f[x]/d[x],其中d[x]表示从x号城市出发的高速公路一共有多少条

能走到y号城市的概率:
	f[y] = Σ(x∈pre(y))f[x]/d[x]
pre(y)表示所有连接到y号城市的高速公路的起点的集合
时间复杂度O(n+m)
*/

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n,m;
std::vector<int>edge[N];
double f[N];
int main()
{	
	cin>>n>>m;
	for(int i = 1; i <= m; i++)
	{
		int x, y;
		cin>>x>>y;
		edge[x].push_back(y);
	}
	memset(f, 0, sizeof(f));
	f[1] = 1;
	for(int i = 1; i <= n; i++)
	{
		int sz = edge[i].size();
		for(auto j : edge[i])
		{
			f[j] += f[i] / sz;
		}
	}
	printf("%.10f\n", f[n]);
	return 0;
}
<div STYLE="page-break-after: always;"></div>

walk2

/*
蜗蜗的世界里有 n 个城市,城市之间通过 m 条单向高速公路连接,初始他在 1 号城市。蜗蜗想去 n 号城市游玩,假设现在他在 x 号城市,他会等概率地选择从 x 出发的高速公路中的一条走过去。如果没有任何从 x 号城里出发的高速公路,他就只能留在原地了。蜗蜗会一直走直到他无路可走。
请问蜗蜗有多大的概率能够走到 n 号城市。

一行一个数表示蜗蜗能够走到 n 号城市的概率。
由于答案是分数,请输出答案 mod 1e9+7。

每行两个整数 x,y(1≤x<y≤n) 描述一条从 x 号城市到 y 号城市的高速公路。


最简分数a/b mod p 的意思是找到整数c使得 bc ≡ a(mod p) 
a/b = c
a = bc
a%p == bc%p
由于p是质数,根据费马小定理,b^(p-1)≡ 1(mod p),也就是说有 b^(p-2) ≡ b^(-1)(mod p),过程中所有
的a/b mod p的操作都可以用a*b^(p-2) mod p代替

b^(p-2)mod p可以用ksm求
能走到y号城市的概率f[y] = Σ(x∈pre(y))f[x]*d[x]^(p-2) mod p
一次ksm时间复杂度log(p)
所以最终时间复杂度O(n+mlog(p))
*/

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1010;
const int p = 1e9 + 7;
int n,m;
std::vector<int>edge[N];
int f[N];
int ksm(int a, int b)
{
	int ans = 1,base = a;
	while(b)
	{
		if(b & 1)
			ans = (ans * base) % p;
		base = (base * base) % p;
		b >>= 1;
	}
	return ans;
}
signed main()
{	
	cin>>n>>m;
	for(int i = 1; i <= m; i++)
	{
		int x, y;
		cin>>x>>y;
		edge[x].push_back(y);
	}
	memset(f, 0, sizeof(f));
	f[1] = 1;
	for(int i = 1; i < n; i++)
	{
		int sz = edge[i].size();
		for(auto j : edge[i])
		{
			f[j] += f[i] * ksm(sz, p - 2);
			f[j] %= p;
		}
	}
	cout<<f[n]<<endl;
	return 0;
}
<div STYLE="page-break-after: always;"></div>

walk3

/*
题意:
蜗蜗的世界里有 n 个城市,城市之间通过 m 条单向高速公路连接,初始他在 1 号城市。蜗蜗想去 n 号城市游玩,假设现在他在 x 号城市,他会等概率地选择从 x 出发的高速公路中的一条走过去。
如果没有任何从 x 号城市出发的高速公路,他就只能留在原地了。蜗蜗会一直走直到他走到 n 号城市。
请问蜗蜗期望经过多少条高速公路能够走到 n 号城市。

输入:
数据保证没有任何两条高速公路的 x,y 是相同的。
数据保证所有城市都可以到 n 号城市。

每行两个整数 x,y(1≤x<y≤n) 描述一条从 x 号城市到 y 号城市的高速公路。


输出:
一行一个数表示蜗蜗期望经过多少条高速公路能够走到 n 号城市。由于答案是分数,请输出答案 mod 1e9+7。

对于期望:
定义:E(X) = Σxipi,即x的加权平均值
期望的性质:
1.E(ax+b) = aE(x)+b;
2.E(x+y) = E(x)+E(y);
3.E(xy) = E(x)E(y)

我们只要分别计算出经过1条,2条,...,n-1条高速公路走到n号城市的概率,就可以算出期望了

用f[i][j]表示经过j条高速公路走到i号城市的概率,有:
	f[i][j] = Σ(x∈pre(i))f[x][j-1]/d[x]
最终答案等于:
	Σ(i=1~n-1)f[n][i]*i
时间复杂度O(n*m log(p))
一次ksm代价log(p)
*/

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1010;
const int p = 1e9+7;
int n,m;
std::vector<int>edge[N];
int f[N][N];
int ksm(int a,int b)
{
	int ans = 1,base = a;
	while(b)
	{
		if(b&1)
			ans = (ans*base)%p;
		base = (base*base)%p;
		b>>=1;
	}
	return ans;
}
signed main()
{	
	cin>>n>>m;
	for(int i = 1;i <= m; i++)
	{
		int x, y;
		cin>>x>>y;
		edge[x].push_back(y);
	}
	memset(f, 0, sizeof(f));
	f[1][0] = 1;//经过0条路走到1号城市概率是1
	for(int i = 1; i < n; i++)
	{
		int sz = edge[i].size();
		for(int k = 0; k < n; k++)//枚举到目前为止走的k条路
			if(f[i][k])
				for(auto j : edge[i])//再枚举接下来去哪
				{
					f[j][k + 1] += f[i][k] * ksm(sz, p - 2);
					f[j][k + 1] %= p;
				}	
	}
	int ans = 0;
	for(int i = 1; i < n; i++)
	{
		ans += f[n][i] * i;
		ans %= p;
	}
	cout<<ans<<endl;
	return 0;
}

/*
能不能更快呢?
考虑从后往前算
用f[x]表示从x号城市出发期望经过多少条高速公路能够到达n号城市,f[n] = 0;
最后的f记录的是从x走到n经过的路的条数
在次考虑期望的定义 E(X) = Σxipi,
有f[x] = Σ(y∈nxt(x))(f[y]+1)/d[x] = Σ(y∈nxt(x))f[y]/d[x]+1
                      x走到y要走一条路,f[y]是y走到终点要走f[y]条路
                      x走到y,y再走到n
nxt(x)表示所有从x出发的高速公路的终点的集合

时间复杂度O(mlog(p))

▲!!!!!
记住求概率是从起点向终点求
要求的是期望从终点向起点求
*/

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1010;
const int p = 1e9+7;
int n, m;
std::vector<int>edge[N];
int f[N];
int ksm(int a, int b)
{
	int ans = 1, base = a;
	while(b)
	{
		if(b & 1)
			ans = (ans * base) % p;
		base = (base * base) % p;
		b >>= 1;
	}
	return ans;
}

signed main()
{
	cin>>n>>m;
	for(int i = 1; i <= m; i++)
	{
		int x, y;
		cin>>x>>y;
		edge[x].push_back(y);
	}

	memset(f, 0, sizeof(f));
	f[n] = 0;
	for(int i = n - 1; i; i--)
	{
		int sz = edge[i].size();
		f[i] = 1;
		for(auto j : edge[i])
		{
			f[i] += f[j] * ksm(sz, p - 2);
			f[i] %= p;
		}
	}
	cout<<f[1]<<endl;
	return 0;
}
<div STYLE="page-break-after: always;"></div>

Backpack

Mixed backpack

//混合背包
/*
有n种物品和一个容量是m的背包。物品一共有三类
第一类01  si=-1
第二类完全 si=0
第三类多重 si>0

处理方法:
分类处理思想:
1.利用多重背包的二进制优化,可以先将多重背包转化为多个01背包
2.用a,b,c三个数组来记录转化之后所有背包的体积、价值、类型,c[i]==0表示完全背包,c[i]==1表示01背包
3.最后做一遍,以c的值分为两类,做完全背包和01背包
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 10010;
int n, m, ty, v, w, s, a[N], b[N], c[N], f[N];
int main()
{
	int num = 1;
	cin>>n>>m;
	for(int i = 1; i <= n; i++)
	{
		cin>>ty>>v>>w;
		if(ty == 2){//完全背包
		 	a[num] = v;
		 	b[num] = w;
		 	c[num++]=0;//背包类型
		}
		else 
		{
			if(ty == 3)//多重
				cin>>s;
			else s = 1;
			int k = 1;//k是拆分系数
			while(s >= k)//二进制拆分
			{
				a[num] = k * v;
				b[num] = k * w;
				c[num++] = 1;//背包类型
				s -= k;
				k <<= 1;//k加倍
			}
			if(s)
			{
				a[num] = s * v;
				b[num] = s * w;
				c[num++] = 1;
			}
		}
	}
	//做一遍两类背包
	for(int i = 1; i < num; i++)
	{
		if(c[i] == 1)//01
		{
			for(int j = m; j >= a[i]; j--)
			{
				f[j] = max(f[j], f[j - a[i]] + b[i]);
			}
		}
		else{//完全
			for(int j = a[i]; j <= m; j++)
			{
				f[j] = max(f[j], f[j - a[i]] + b[i]);
			}
		}
	}
	cout<<f[m]<<endl;
	return 0;
}


/*
1.根据s的值分别处理三类背包,多吃背包可以用单调队列优化
2.可以把三类背包封装为三个函数
ZeroOnePack(int v,int w)
CompletePack(int v,int w)
MultiplePack(int v,int w,int s)

那么主函数
int v,w,s;
for(int i =1;i<=n;i++)
{
	if(s==-1)
		ZeroOnePack(v,w);
	else if(s==0)
		CompletePack(v,w)
	else
		MultiplePack(v,w,s)

}
*/
<div STYLE="page-break-after: always;"></div>

Partial backpack

#include<bits/stdc++.h>
using namespace std;

struct ty
{
	double m, v;
}a[110];

double res[110];

bool cmp(ty a,ty b)
{
	return a.v / a.m > b.v / b.m;
}

int main()
{
	int n, t;
	cin>>n>>t;
	for(int i = 1; i <= n; i++)	cin>>a[i].m>>a[i].v;
	sort(a + 1, a + 1 + n, cmp);
    double ans = 0;
    for(int i = 1; i <= n; i++)
    {
    	if(a[i].m <= t)	ans += a[i].v, t -= a[i].m;
    	else
    	{
    		ans += a[i].v * t * (1.0) / (a[i].m * (1.0));
    		break;
    	}
    }
    printf("%.2f\n", ans);
	return 0;
}
<div STYLE="page-break-after: always;"></div>

Pack pack

写法1

//分组背包
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int n, V, f[N];
std::vector<pair<int,int>>a[N];
int main()
{
	cin>>n>>V;
	for(int i = 1; i <= n; i++)
	{
		int t;
		cin>>t;
		while(t--)
		{
			int v, w;
			cin>>v>>w;
			a[i].push_back({v, w});
		}
	}
	for(int k = 1; k <= n; k++)
	{
		for(int i = V; i >= 0; i--)
		{
			for(auto x : a[k])
			{
				if(i >= x.first)
					f[i] = max(f[i], f[i - x.first] + x.second);
			}
		}
	}
	cout<<f[V]<<endl;
	return 0;
}
<div STYLE="page-break-after: always;"></div>

写法2

//n<=1000
#include<bits/stdc++.h>
using namespace std;
const int N = 1100;
int n, m, v[N], w[N], a[N], f[N];
std::vector<int> c[N];

int main()
{
	cin>>n>>m;
	for(int i = 1; i <= n; i++)
	{
		cin>>a[i]>>v[i]>>w[i];
		c[a[i]].push_back(i);//记录编号
	}
	for(int i = 1; i <= 1000; i++)
	{
		for(int j = m; j >= 0; j--)//先枚举体积
		{
			for(auto k : c[i])//枚举第i组选的情况
			{
				if(v[k] <= j)
					f[j] = max(f[j], f[j - v[k]] + w[k]);
			}
		}
	}
	cout<<f[m];
}

/*
5 10
1 1 6
1 6 9
1 5 6
2 4 5
2 5 10

16
*/

标签:std,师妹,int,ll,pos,ans,整理,dp
From: https://www.cnblogs.com/magicat/p/17394093.html

相关文章

  • 肖sir___项目讲解__整理电商后台
    电商我讲下我做的xx电商项目:我这个平台有后端、用户端、商家端;我先讲下后端管理包括:首页、商品管理、订单管理、地址管理、系统管理首页:控制台统计包括:平台实时交易情况(平台资金总额、今日交易总额、订单成交数量)、当日成交金额、类目销售金额占比、入账统计 商品管理:首......
  • Cache相关知识整理
    高速缓存的基本原理参考资料:CSAPP相关章节GalleryofProcessorCacheEffects(igoro.com)Makeyourprogramsrunfasterbybetterusingthedatacache-Johnny'sSoftwareLab在物理结构上,Cache由SRAM构成,SRAM比DRAM的速度快一一些,但是造价会更高。高速缓存的结......
  • MongoDB整理
    MongoDB一、数据库(database)①什么是数据库?存储数据的仓库②为什么要有数据库?数据持久化③数据库能做什么?存储数据,可以通过网络访问④数据库的分类按照关系型分类:1、关系型数据库(MySQL、Oracle等)2、非关系型数据库(MongoDB、Redis)区别:关系型是创建表格,非关系型......
  • 最近VJ刷题整理
    BitsReverse题意:给出两个数,每次操作其中一个数,将其二进制位上连续的三个数翻转,求最小的操作次数Solution每次操作相当于交换了左右两个二进制位的数,所以一次操作只会改变奇数位/偶数位上的数,考虑到只用求最小的操作次数,我们可以将每个数的二进制位上的1所在的位置分奇偶存一下......
  • FastReport问题整理(转载)
    1.FastReport中如果访问报表中的对象?可以使用FindObject方法。TfrxMemoView(frxReport1.FindObject(’memo1′)).Text:=’FastReport’;2.FastReport中如何使用上下标?设置frxmemoview.AllowHTMLTags:=True;在Text输入如下上标:mm<sup>2</sup>下表:k<sub>6</sub>举一反三,你还可以......
  • POE供电资料整理
    使用芯片TPS23861PWR供应商链接:https://item.szlcsc.com/94440.htmlTPS23861具有自主模式的2线对、2类、4通道PoEPSE官网链接:https://www.ti.com.cn/product/cn/TPS23861?qgpn=tps23861DataSheet:https://www.ti.com.cn/cn/lit/gpn/tps23861评估板页面TPS23861EVM-6......
  • SpringMVC常用注解整理
    一、组件型注解:@Component在类定义之前添加@Component注解,他会被spring容器识别,并转为bean。@Repository对Dao实现类进行注解(特殊的@Component)@Service用于对业务逻辑层进行注解,(特殊的@Component)@Controller用于控制层注解,(特殊的@Component)以上四种注解都是......
  • Oracle日期处理整理
    1.获取日期元素注意:1).hh24写法指24小时,Oracle默认是12小时2).分钟用mi,不要用mm,因为与之前的MM冲突1-12小时写法yyyyMMdd24miss(Oracle默认)1-24小时写法yyyyMMddHH24miss获取日期元素:selectto_char(sysdate,'yyyy-mm-ddhh24:mi:ss')fromdual;--日期转化为字......
  • Leetcode11~20题整理
    11.盛最多水的容器比较暴力的做法:classSolution{public:intmaxArea(vector<int>&h){vector<int>t;intn=h.size();intres=-1;for(inti=0;i<n;i++){for(intj=0;j<(int)t.size(......
  • 自动驾驶产业链调研之主机厂、软件方案商、硬件方案商 , 超详细的自动驾驶产业链调研,该
    自动驾驶产业链调研之主机厂、软件方案商、硬件方案商,超详细的自动驾驶产业链调研,该文件主要整理车企、Tier1主机厂、自动驾驶软件方案商、自动驾驶硬件方案商,在以下维度进行的调研整理,包括自动驾驶方面的发展路径、技术方案、技术合作伙伴、调研结论汇总。倘若你是产品经理......