【联赛级别的DP】

概念

通过局部最优来推导出全局最优。

适用范围

1、具有最优性的子结构,一个大的最优状态必须包含着一种最优的子状态。
2、具有重叠计数性。
3、每一个子问题需要相对独立
4、存在问题的边界,可以停止DP。

基本方法

我的方法是:
1、先针对题目的条件设DP。
2、判断DP 是否满足无后效性、独立性(阶段性)。

例题

对于DP类的问题,光是将概念是无法将它理解的,我们需要在题目中真切的感受到DP思维的强大。

最长不下降子序列

题目

题目描述
有长度为N的序列:(n<=1000)
A1 A2 …..An
求最长不下降子序列:Ai1,Ai2,,,,,Aik, 其中ai1<=ai2<=…..<=aik
求最长不下降子序列的长度

输入

输出

样例输入
11
1 3 6 3 4 7 5 7 6 7 8

样例输出
8

方法

很简单,不用说了。
设f[i]选到了第i个数的最长不下降子序列长度。

f[i]=f[j]+1(j<ia[i]>=a[j])f[i]=f[j]+1(j<i且a[i]>=a[j])

答案就是f[n].

程序

var
        n,i,j,max:longint;
        a,f:array[1..1000] of longint;
begin
        read(n);
        for i:=1 to n do
          read(a[i]);
        for i:=1 to n  do
          f[i]:=1;
        for i:=1 to n do
          begin
            for j:=1 to i-1 do
              if a[i]>=a[j] then
                begin
                  if f[i]<f[j]+1 then
                    f[i]:=f[j]+1;
                end;
          end;
        max:=-maxlongint;
        for i:=1 to n do
          if max<f[i] then
            max:=f[i];
        writeln(max);
end.

【NOI2013模拟】Number (Standard IO)

题目

Time Limits: 1000 ms Memory Limits: 65536 KB Detailed Limits

Description

JYY 来到了一个新的城市,为了和大家保持联系,第一件事就是办理新的手机号。JYY 对号码的要求很高,希望大家都能够顺畅地阅读手机号,因此 JYY 特别研究了地球人的电话号码阅读习惯,有如下重大发现 (请自行代入你们的手机号码):地球人总是按照以下四种方式来朗读电话号码:

  1. xxx-xxx-xxxxx 例如 151-958-83019

  2. xxx-xxxx-xxxx 例如 151-9588-3019

  3. xxxx-xxxx-xxx 例如 1519-5883-019

  4. xxxx-xxx-xxxx 例如 1519-588-3019

即便是同一个电话号码,不同的人也会按照自己的习惯去解读,例如有些人会觉得电话号码是151-9588-3019,而有些却坚持 1519-588-3019。

为了让号码显得更上口,JYY 认为有些不吉利的数字串,在报电话号码时永远不应该被完整地读出来,例如,JYY 认为 7456 是不吉利的数字串 (气死我了),那么 13000007456 就是一个不好的号码,因为有人会把这个号码读作 130-000-07456,而在最后一次完整地读出了 7456。然而,13000745600 却不是一个不好的号码,因为无论怎么读,7456 都会被拆分开来。

现在给出 JYY 认为不吉利的数字串,请你计算满足 JYY 要求的电话号码,总共有多少个。具体来说说,一个好的电话号码满足以下要求:

  1. 电话号码是以 1 开头的 11 位数字串。

  2. 这个号码无论按照之前描述的 4 种读法中的哪个,都不能在任意时刻连续读出不吉利的数字串。

Input

输入文件第一行包括一个整数 n,表示 JYY 认为不吉利的数字串的数量。

接下来 n 行,每行一个不超过 5 的数字串,表示一个 JYY 认为不吉利的号码。

Output
输出一行,为满足条件的电话号码总数。

Sample Input
1

7456

Sample Output
9996000100

Data Constraint
40% 的数据满足 n <= 2。

100% 的数据满足 1 <= n <= 100。

题解

我们可以考虑dp.
设fi,j表示i~i+3位数字填的状态是j(j 是10进制数)
我们可以将j拆成四位数字a1,b1,c1,d1。第i位是a1、第i+1位是b1、第i+2位是c1、第i+3位是d1.
我们枚举第i+4位e1.
我们就可以得到转移:

fi+1,b11000+c1100+d110+e1=fi,a11000+b1100+c110+d1+1fi+1,b1∗1000+c1∗100+d1∗10+e1=fi,a1∗1000+b1∗100+c1∗10+d1+1(当情况允许时)

而情况允许的条件就是,满足于e1连起来的状态(如:d1*10+e1……)中没有不合法的。
这样,答案就是
f8,n∑f8,n

代码

#include<cstdio>
#include<cstring>
using namespace std;
long long f[9][10000];
long long bz[6][100001],n;
char st[101][6];
int main()
{
    scanf("%d\n",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%s\n",&st[i]);
        int p=0;
        int len=strlen(st[i]);
        for (int j=0;j<=len-1;j++)
        {
            p=p*10+st[i][j]-'0';
        }
        bz[len][p]++;
    }
    memset(f,0,sizeof(f));
    for (int j=0;j<10000;j++)
    {
        int a1=j/1000%10;
        if (a1!=1) continue;
        int b1=j/100%10;
        int c1=j/10%10;
        int d1=j%10;
        int p=a1;
        if (bz[1][p]!=0) continue;
        p=b1;
        if (bz[1][p]!=0) continue;
        p=c1;
        if (bz[1][p]!=0) continue;
        p=d1;
        if (bz[1][p]!=0) continue;
        p=a1*10+b1;
        if (bz[2][p]!=0) continue;
        p=b1*10+c1;
        if (bz[2][p]!=0) continue;
        p=c1*10+d1;
        if (bz[2][p]!=0) continue;
        p=a1*100+b1*10+c1;
        if (bz[3][p]!=0) continue;
        p=b1*100+c1*10+d1;
        if (bz[3][p]!=0) continue;
        p=a1*1000+b1*100+c1*10+d1;
        if (bz[4][p]!=0) continue;
        f[1][j]=1;
    }
    int total=0;
    for (int i=1;i<=7;i++)
    {
        for (int j=0;j<10000;j++)
        {
            if (f[i][j]==0) continue;
            int a1=j/1000%10;
            int b1=j/100%10;
            int c1=j/10%10;
            int d1=j%10;
            int wei=i+4;
            for (int e1=0;e1<=9;e1++)
            {
                if (bz[1][e1]!=0) continue;
                if (bz[2][d1*10+e1]!=0) continue;
                if (wei!=5)
                {
                    if (bz[3][c1*100+d1*10+e1]!=0) continue;
                }
                if (wei!=6&&wei!=9&&wei!=5)
                {
                    if (bz[4][b1*1000+c1*100+d1*10+e1]!=0) continue;
                }
                if (wei==11)
                {
                    if (bz[5][a1*10000+b1*1000+c1*100+d1*10+e1]!=0) continue;
                }
                f[i+1][b1*1000+c1*100+d1*10+e1]+=f[i][j];
            }
        }
    }
    long long ans=0;
    for (int j=0;j<10000;j++)
    {
        ans+=f[8][j];
    }
    printf("%lld",ans);
}

number

题目

(File IO): input:number.in output:number.out
Time Limits: 1000 ms Memory Limits: 262144 KB Detailed Limits

Description
给定正整数 n,m,问有多少个正整数满足: (1)不含前导 0; (2)是 m 的倍数; (3)可以通过重排列各个数位得到 n。

Input
一行两个整数 n,m。

Output
一行一个整数表示答案对 998244353 取模的结果。

Sample Input
1 1

Sample Output
1

Data Constraint
对于 20%的数据,n<10^10。
对于 50%的数据,n<10^16,m<=20。
对于 100%的数据,n<10^20,m<=100。

题解

我们可以设fi,j,k为当前选到第i位,二进制压位n选的状态为j,对m的取模为k的方案数
设p代表新选的数。

fi+1,j+p,(k10+p)modm=fi,j+1fi+1,j+p,(k∗10+p)modm=fi,j+1

答案为
fn,2n1,0fn,2n−1,0

这时我们会发现,这个时间复杂度会很大(答案还可能会计重)。
于是,我们可以考虑一下优化:
1、如果状态j中有值的位不是i个,那么可以直接判断这种情况是不合法的。这样,我们也可以省去一维i(指的是在数组里省去,时间复杂度不变)。
2、我们会发现j中有一些奇怪的潜规则:对于两个相同的数字,它的地位却是因位置而定的。这样就会导致一些多余重复计算的状态出现。所以,我们可以考虑删去这些状态。现将n的各个位数排序。如果满足存在
ai=ai1statei1=0statei0ai=ai−1且statei−1=0且statei≠0
则这个状态是不合法的。这样,我们的状态数就会从2^20降到3^10.
3、但是,我们会发现,这样的时间复杂度还是太大,所以我们需要再度进行优化。我们可以预处理出每一个状态第一个出现
ai=jstatei=0ai=j且statei=0
的位置。然后直接枚举加入哪一个数字。将第一个出现他的位置填满。
这样的时间复杂度是:O(3^10*n*10*m)。
就可以拿到100分。

代码

#include<cstdio>
#include<cstring>
using namespace std;
char s[21];
long long f[59050][101];
int x,m,a[21],n,mi2[21],pd[1048577],st[1048577][21],bo[21][2];
bool bz[1048577];
void zhuan_2(int k)
{
    int len=n;
    int p=k;
    while (k>0)
    {
        st[p][len]=k%2;
        if (st[p][len]==1) st[p][0]++;
        k=k/2;
        len--;
    }
}
void qsort(int l,int r)
{
    int i=l;
    int j=r;
    int mid=a[(i+j)/2];
    while (i<=j)
    {
        while (a[i]<mid) i++;
        while (a[j]>mid) j--;
        if (i<=j)
        {
            int t=a[i];
            a[i]=a[j];
            a[j]=t;
            i++;
            j--;
        }
    }
    if (i<r) qsort(i,r);
    if (l<j) qsort(l,j);
}
int main()
{
    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);
    scanf("%s",&s);
    scanf("%d",&m);
    n=strlen(s);
    for (int i=0;i<=n-1;i++)
    {
        a[i+1]=s[i]-'0';
    }
    qsort(1,n);
    mi2[0]=1;
    for (int i=1;i<=n;i++)
    {
        mi2[i]=mi2[i-1]*2;  
    }
    for (int i=0;i<=mi2[n]-1;i++)
    {
        zhuan_2(i);
    }
    memset(bz,true,sizeof(bz));
    int total=0;
    for (int i=0;i<=mi2[n]-1;i++)
    {
        for (int j=2;j<=n;j++)
        {
            if (a[j]==a[j-1])
            {
                if (st[i][j-1]==0&&st[i][j]!=0)
                {
                    bz[i]=false;
                    break;
                }
            }
        }
        if (bz[i]==true)
        {
            total++;
            pd[i]=total;
        }
    }
    f[pd[0]][0]=1;
    int oo=0;
    for (int j=0;j<=mi2[n]-1;j++)
    {   
        if (bz[j]==false) continue;
        for (int i=0;i<=n;i++)
        {
            if (i!=st[j][0]) continue;
            oo++;
            for (int k=1;k<=n;k++)
            {
                if (bo[a[k]][1]!=oo&&st[j][k]==0)
                {
                    bo[a[k]][1]=oo;
                    bo[a[k]][0]=k;
                }
            }
            for (int k=0;k<=9;k++)
            {
                if (i==0&&k==0&&i+1!=n) continue;
                x=j;
                if (bo[k][1]==oo)
                {
                    x=x|mi2[n-bo[k][0]];
                    if (x==j) continue;
                    for (int p=0;p<=m;p++)
                    {
                        f[pd[x]][(p*10+k)%m]+=f[pd[j]][p];
                        f[pd[x]][(p*10+k)%m]%=998244353;
                    }
                }
            }
        }
    }
    printf("%lld",f[pd[mi2[n]-1]][0]);
}
内容概要:本文针对火电厂参与直购交易挤占风电上网空间的问题,提出了一种风火打捆参与大用户直购交易的新模式。通过分析可再生能源配额机制下的双边博弈关系,建立了基于动态非合作博弈理论的博弈模型,以直购电价和直购电量为决策变量,实现双方收益均衡最大化。论文论证了纳什均衡的存在性,并提出了基于纳什谈判法的风-火利益分配方法。算例结果表明,该模式能够增加各方收益、促进风电消纳并提高电网灵活性。文中详细介绍了模型构建、成本计算和博弈均衡的实现过程,并通过Python代码复现了模型,包括参数定义、收益函数、纳什均衡求解、利益分配及可视化分析等功能。 适合人群:电力系统研究人员、能源政策制定者、从事电力市场交易的工程师和分析师。 使用场景及目标:①帮助理解风火打捆参与大用户直购交易的博弈机制;②为电力市场设计提供理论依据和技术支持;③评估同政策(如可再生能源配额)对电力市场的影响;④通过代码实现和可视化工具辅助教学和研究。 其他说明:该研究仅提供了理论分析,还通过详细的代码实现和算例验证了模型的有效性,为实际应用提供了参考。此外,论文还探讨了同场景下的敏感性分析,如证书价格、风电比例等对市场结果的影响,进一步丰富了研究内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值