疯狂刷dp中

数字游戏
这是一个很好的区间dp题,个人感觉,容易想到,但是不容易写出来,边界处理很强,预处理出,前缀和,最小值需要初始化,破坏环成为两倍的大小的数组,细节很多。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e5 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double  lnf  = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const int p=1e7+233;
const  double pi=3.141592653589;
int dpma[105][105][12],dpmi[105][105][12];
int a[105];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        a[i+n]=a[i];//破坏环
    }
    ms(dpmi,inf);
    for(int i=2;i<=2*n;i++)a[i]+=a[i-1];//前缀和
    for(int i=1;i<=2*n;i++)
    {
        for(int j=i;j<=2*n;j++)
        {
            dpma[i][j][1]=dpmi[i][j][1]=((a[j]-a[i-1])%10+10)%10;//预处理
        }
    }
    for(int i=2;i<=m;i++)//枚举断点个数
    {
        for(int l=1;l<=2*n;l++)//左端点
        {
            for(int r=l+i-1;r<=2*n;r++)//右端点
            {
                for(int k=l+i-2;k<r;k++)//枚举断点位置
                {
                    dpma[l][r][i]=max(dpma[l][k][i-1]*(((a[r]-a[k])%10+10)%10),dpma[l][r][i]);
                    dpmi[l][r][i]=min(dpmi[l][k][i-1]*(((a[r]-a[k])%10+10)%10),dpmi[l][r][i]);
                }
            }
        }
    }
    int ma=0,mi=inf;
    for(int i=1;i<=n;i++)
    {
        ma=max(ma,dpma[i][i+n-1][m]);
        mi=min(mi,dpmi[i][i+n-1][m]);
    }
    printf("%d\n%d\n",mi,ma);
    return 0;
}

P1052 过河

通过这个题我学到许多,这种路径压缩是真的强,放在我是想不到,我想的是离散,然而离散的话就不好控制特么之间的距离,路径压缩我感觉是离散的一种拓展,和离散十分的相似(个人感觉)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e5 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double  lnf  = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const  double pi=3.141592653589;
int a[105],flage[maxn],dp[maxn];//看好空间大小啊
int main()
{
    int L;
    int S,T,M;
    scanf("%d%d%d%d",&L,&S,&T,&M);
    if(S==T)//一定要特判啊
    {
        int x,cnt=0;
        for(int i=1;i<=M;i++)
        {
            scanf("%d",&x);
            cnt+=(x%S==0);
        }
        printf("%d\n",cnt);
        return 0;
    }
    for(int i=1;i<=M;i++)
    {
        scanf("%d",&a[i]);
    }
    sort(a+1,a+1+M);
    a[M+1]=L;
    ms(dp,inf);
    dp[0]=0;
    a[0]=0;
    int p=0;
    for(int i=1;i<=M;i++)
    {
        int far=min(90,a[i]-a[i-1]);//如果想看证明过程的话请到洛谷去看,可以通过上面的链接进入,也可以自己去搜索,进去后第一个题解有详细证明过程。其实只要开到(T+1)*T-T-(T+1)也就是71就足够了
        p+=far;
        flage[p]=1;
    }
    p+=min(90,L-a[M]);
    for(int i=0;i<=p+T-1;i++)
    {
        for(int j=S;j<=T;j++)
        {
            if(i-j>=0)
            dp[i]=min(dp[i],dp[i-j]+flage[i]);
        }
    }
    int ans=inf;
    for(int i=p;i<=p+T-1;i++)//因为你可能是条过了桥,可能从L-1跳到L-1+(S到T内的一个数),也可能是l-2+(S到K内的一个数),所以答案在p到p+T-1内
    {
        ans=min(ans,dp[i]);
    }
    printf("%d\n",ans);
    return 0;
}

P1095 守望者的逃离

本题主要是要想到把位移与休息和跑步分开分别求一次dp,这样就十分简单了

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =3e5 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double  lnf  = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const  double pi=3.141592653589;
int dp[maxn];
int main()
{
    int m,s,t;
    scanf("%d%d%d",&m,&s,&t);
    for(int i=1;i<=t;i++)
    {
        if(m>=10)
            dp[i]=dp[i-1]+60,m-=10;
        else
            m+=4,dp[i]=dp[i-1];
    }
    for(int i=1;i<=t;i++)
    {
        dp[i]=max(dp[i],dp[i-1]+17);
    }
    if(dp[t]<s)
    {
        printf("No\n%d\n",dp[t]);
    }
    else
    {
        printf("Yes\n");
        for(int i=1;i<=t;i++)
        {
            if(dp[i]>=s)
            {
                printf("%d\n",i);
                return 0;
            }
        }
    }
    return 0;
}

P1108 低价购买

没想到这么容易的一个题目我看题解都看了半天,我也是醉了,还是不喜欢独立思考啊,这毛病要改啊,

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =3e5 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double  lnf  = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const  double pi=3.141592653589;
int dp[maxn],n,a[maxn],f[maxn];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int ma=1;
    for(int i=1;i<=n;i++)
    {
        dp[i]=1;
        for(int j=1;j<i;j++)
        {
            if(a[i]<a[j])
            {
                dp[i]=max(dp[i],dp[j]+1);
                ma=max(ma,dp[i]);
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(dp[i]==1)f[i]=1;
        for(int j=1;j<i;j++)
        {
            if(dp[i]==dp[j]+1&&a[i]<a[j])//如果dp[j]+1==dp[i]并且a[i]
            {
                f[i]+=f[j];
            }
            else if(dp[i]==dp[j]&&a[i]==a[j])//如果两个都相等的话舍弃一个对结果没有影响
                f[j]=0;
        }
    }
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        if(dp[i]==ma)sum+=f[i];
    }
    printf("%d %d\n",ma,sum);
    return 0;
}

P1099 树网的核
本题是树形dp之类的题目很容易,但是又不好想,首先树的直径肯定是要求的,然后在树的直径上尺取法找最大值中的最小值,但是要考虑的是,如果s大于树的直径的话,那么这样求出来的可能就是0了,但是如果他还有分支的话,那么绝对不可能是0,所有还要求其他的点到树的直径的距离和答案取个最大值就可了。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e3 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double  lnf  = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const  double pi=3.141592653589;
int n,s,pa[maxn],max_len=0,dis[maxn],k=0,vis[maxn];
vector<pair<int,int> >ve[maxn];
void dfs(int u,int f)
{
   pa[u]=f;
   if(dis[k]<dis[u])k=u;
   for(int i=0;i<ve[u].size();i++)
   {
       int to=ve[u][i].first;
       if(to==f||vis[to])continue;
       dis[to]=dis[u]+ve[u][i].second;
       dfs(to,u);
   }
}
int main()
{
    scanf("%d%d",&n,&s);
    for(int i=1;i<n;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        ve[u].push_back({v,w});
        ve[v].push_back({u,w});
    }
    dis[1]=0;
    dfs(1,-1);
    int st=k;
    dis[k]=0;
    dfs(k,-1);
    int ed=k;
    int ans=inf;
    for(int i=ed,j=ed;i+1&&j+1;i=pa[i])
    {
        while(dis[j]-dis[i]>s)j=pa[j];
        ans=min(ans,max(dis[i],dis[ed]-dis[j]));
    }
    for(int i=ed;i+1;i=pa[i])vis[i]=1;
    for(int i=ed;i+1;i=pa[i])
    {
        k=i,dis[k]=0;
        dfs(i,pa[i]);
    }
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,dis[i]);
    }
    printf("%d\n",ans);
    return 0;
}

P3399 丝绸之路
,本题我采用的是单调队列优化dp,但是我看题解没有写的这么麻烦,但是做出来了就是王道,其实三重循环加o2也可以过,不加o2直接超时,加了单调队列100ms,还是可以接受的,我看其他人的就50ms左右得去学习一下。
,不加单调队列的暴力写法

  for(int i=1;i<=n;i++)
    {
        for(int j=i;j<=m;j++)
        {
            for(int k=i-1;k<=j-1;k++)
            dp[i][j]=min(dp[i-1][k]+a[i]*b[j],dp[i][j]);
        }
    }

,加了单调队列的写法

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e3 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double  lnf  = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const  double pi=3.141592653589;
int a[maxn],dp[1005][1005],b[maxn];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&b[i]);
    }
    ms(dp,inf);
    for(int j=0;j<=m;j++)
        dp[0][j]=0;
    deque<int>q;
    for(int i=1;i<=n;i++)
    {
        q.push_back(i-1);
        for(int j=i;j<=m;j++)
        {
            dp[i][j]=min(dp[i-1][q.front()]+a[i]*b[j],dp[i][j]);
            while(dp[i-1][q.front()]>=dp[i-1][j]&&!q.empty())
                q.pop_back();
            q.push_back(j);
            //bug;
        }
        while(!q.empty())q.pop_back();
    }
    int ans=inf;
    for(int j=n;j<=m;j++)
        ans=min(ans,dp[n][j]);
    printf("%d\n",ans);
    return 0;
}

.哇哦,我取经回来了原来是我定义的dp[i][j]是到达第i个城市用了j天,大佬的是第i天到达第j个城市代码直接给上

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e3 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double  lnf  = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const  double pi=3.141592653589;
int a[maxn],b[maxn],dp[1005][1004];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    for(int j=1;j<=m;j++)
    {
        scanf("%d",&b[j]);
    }
    ms(dp,inf);
    dp[0][0]=0;
    for(int i=1;i<=m;i++)
    {
        for(int j=0;j<=min(n,i);j++)
        {
            if(j==i)dp[i][j]=dp[i-1][j-1]+a[j]*b[i];
            else
            dp[i][j]=min(dp[i-1][j],dp[i-1][j-1]+a[j]*b[i]);//dp[i-1][j]代表第i天休息但已经到达j,            与第i-1天走到j花费的最小值
        }
    }
    int ans=inf;
    for(int i=n;i<=m;i++)
        ans=min(ans,dp[i][n]);
    printf("%d\n",ans);
    return 0;
}

POJ - 1651
,最近唯一自己写出来的dp题目很容易,而且还是自己写过一个类似的题目才写出来的,,要加油了啊,不要拖后腿啊,由于是自己写出来的就不写题解了,直接上代码吧

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e6+10;
const int maxm = 1.5e5+50;
const double eps = 1e-18;
const double inf = 0x3f3f3f3f;
const double  lnf  = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const  double pi=3.141592653589;
int a[1005],dp[1005][1005];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    ms(dp,inf);
    for(int i=1;i<=n;i++)
    {
        dp[i][i+2]=a[i]*a[i+1]*a[i+2];
        dp[i][i]=dp[i][i+1]=0;
    }
    for(int len=3;len<=n;len++)
    {
        for(int l=1;l+len-1<=n;l++)
        {
            int r=len+l-1;
            for(int k=l+1;k<r;k++)
            {
                dp[l][r]=min(dp[l][r],dp[l][k]+dp[k][r]+a[l]*a[k]*a[r]);
            }
        }
    }
    printf("%d\n",dp[1][n]);
    return 0;
}

HDU - 2476
,还是区间dp问题,不过这一个思路倒是没错,但是写的顺序撮了第一个dp方程一定要写出来很经典,第二个就要想一想了

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e6+10;
const int maxm = 1.5e5+50;
const double eps = 1e-18;
const double inf = 0x3f3f3f3f;
const double  lnf  = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const  double pi=3.141592653589;
char a[105],b[105];
int dp[105][105],f[105];
int main()
{
    while(~scanf("%s%s",a+1,b+1))
    {
        int n=strlen(a+1);
        ms(dp,0);
        for(int i=1; i<=n; i++)dp[i][i]=1;
        for(int len=2; len<=n; len++)
        {
            for(int l=1; l+len-1<=n; l++)
            {
                int r=l+len-1;
                if(b[l]==b[r])
                    dp[l][r]=min(dp[l][r-1],dp[l+1][r]);
                else
                    dp[l][r]=min(dp[l][r-1]+1,dp[l+1][r]+1);
                for(int k=l; k<=r; k++)
                {
                    dp[l][r]=min(dp[l][k]+dp[k+1][r],dp[l][r]);
                }
            }
        }
        for(int i=1;i<=n;i++)
            f[i]=dp[1][i];
        for(int i=1;i<=n;i++)
        {
            if(a[i]==b[i])
            {
               f[i]=f[i-1];
            }
            else
            {
                for(int k=1;k<i;k++)
                {
                    f[i]=min(f[i],f[k]+dp[k+1][i]);
                }
            }
        }
        printf("%d\n",f[n]);
    }
    return 0;
}

You Are the One HDU - 4283
本题还是区间dp的题我还是看了好久的题解,害写出来以后发现就那样,枚举区间长度,枚举左端点,找到有端点,枚举断点没了。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =2e2+10;
const int maxm = 1.5e5+50;
const double eps = 1e-18;
const double inf = 0x3f3f3f3f;
const double  lnf  = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const  double pi=3.141592653589;
int a[maxn],dp[maxn][maxn],sum[maxn];
int main()
{
    int T;
    scanf("%d",&T);
    int cas=1;
    while(T--)
    {
        ms(dp,inf);
        ms(sum,0);
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            dp[i][i]=0;
            sum[i]=sum[i-1]+a[i];
        }
        for(int len=2;len<=n;len++)
        {
            for(int l=1;l+len-1<=n;l++)
            {
                int r=len+l-1;
                dp[l][r]=min(dp[l][r],dp[l+1][r]+(r-l)*a[l]);
                for(int k=l;k<=r;k++)
                {
                    dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+(k-l+1)*(sum[r]-sum[k]));//这里因为要把dp[k+1][r]和之前的那个区间合并,所以之前的那个区间先是全部上完了,然后这个区间再上,所以要加上,上一个区间的区间长度乘以他们的屌丝值,
 //dp[i][j](只考虑i到j这个区间内值不考虑其他的,也就是说现在只有i到j这么多人在参见这个活动)代表i到j内屌丝值乘以上场顺序的最小值
                }
            }
        }
        printf("Case #%d: %d\n",cas++,dp[1][n]);
    }
    return 0;
}

你可能感兴趣的:(acm暑训,dp)