周六训练:2013 ACM-ICPC长沙赛区全国邀请赛

本文解析了HDU4565-4575题目的解题思路与代码实现,重点介绍了DP算法的应用,包括解决挑战顺序问题的DP策略、最短路径结合状态压缩DP的方法及复杂度处理技巧。

PS: 欠了好多期训练我都没有总结了。。。。。
好难受啊,一直准备贴个dp的训练集合出来。
hdu题号:
HDU 4565 —-4575

这里写图片描述

A
数学题: 精度问题很坑,小学弟的精度很高没搞过,然后调低精度a了。。。。。

B
全场都没人过。。。。 好像不会

C
一个比较难想的dp。。。
题意:给定所有人初识的排列,以及所有人将会挑战(和他前一位进行战斗)赢的次数。。。如果赢的话,他们会交换位置。
问:给的情况是否能够成立, 如果成立是否有多重结果?
比如:
3
0 1 1
可以有如下几种结果: 123
3和2挑战->132 , 2和3挑战->123
2和1挑战->213 , 3和1挑战->231

这个题的关键问题是:我们不知道先执行谁的挑战
但可以知道:如果某个人的ai>i-1 ,那么他必须要先向后移动
这就是解题的关键

我们设 dp[i] 表示i+1 超过前面i 个人,到达第一名需要的总次数。
则dp[i]=dp[i-1]+v[i]+1;

如果某个人的V 比超过前面所有人(dp[i])都还多,记录为k。我们只需要管最后k即可,因为最后一个可以满足前面所有的k。
那么我们接下来只需要知道 这个k最多能够向后移动多少位。 如果向后移动的次数+dp[i]

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) {
    return b ? gcd(b, a % b) : a;
}
template<class T> T lcm(T a, T b) {
    return a / gcd(a, b) * b;
}
using namespace std;
#define ll __int64
ll  dp[1000005],v[1000005];
ll  r;
int main()
{
    //freopen("1.txt","r",stdin);
    ll n,k,b;
    int t=0;
    while(~scanf("%I64d",&n))
    {
        t++;
        if(t>=40) continue;
        memset(dp,0,sizeof dp);   //
        for(int i=1;i<=n;i++)
        {
            scanf("%I64d",&v[i]);
            dp[i]=dp[i-1]+v[i]+1;
            if(dp[i-1]<=v[i])
                k=i;       
//            printf("dp=%I64d\n",l[i]);
        }
        r=b=0;
        for(int i=k+1;i<=n;i++)
        {
            if(v[i]<=b) b++;  // 如果出现0 ,那么会减少贡献值(后面还有多少个v就减少多少个)。
            r+=max((ll)0,v[i]-b); // 可以向后移动r ,0不会产生贡献值
        }
        if(r==v[k]-dp[k-1]) printf("%I64d\n",k);
        else if(r<v[k]-dp[k-1]) printf("Bad Rescue\n");
        else printf("Unknown\n");
    }
    return 0;
}

D
熊神说是最短路然后用状压dp做一做。抱大腿的感觉真好

E
数学题,嗨呀,抱大腿的感觉真好

F
dp,看见的比较晚,写了半个小时就过了。
题意: 给一串数字,可以分成几段:
ans=每一段首项a * 2^(这一段的长度) 求和
我们设 dp[i][j]:表示将前i个数字分成j段的最小值
有一个坑的地方时
长度=64,所以有些地方会爆longlong,所以要注意这些特殊情况的处理。

using namespace std;
#define ll __int64
/*
 */

ll  dp[100][100];  //前i个分成j段的最小值
ll a[105];
ll pp[60];
void init(){  //
    ll cnt=2;
    for(int i=1;i<=62;i++){
        pp[i]=cnt;
        cnt*=2;
//      printf("i=%d %I64d\n",i,pp[i]);
    }
}
int main() {
    //freopen("1.txt","r",stdin);
    int w;
    init();
    scanf("%d",&w);
//  int cas=1;
    while(w--){
        int  n;
        memset(dp,63,sizeof(dp));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%I64d",&a[i]);
        dp[1][1]=a[1]*2;
        for(int i=2;i<=n;i++){
            for(int j=1;j<i;j++){
                for(int k=1;k<=j+1;k++){
                    if(k==1){
                        if(i<=62)
                            dp[i][1]=a[1]*pp[i];
                        continue;
                    }
                    if(i-j<=62)
                        dp[i][k]=min(dp[j][k-1]+a[j+1]*pp[i-j],dp[i][k]);
                    if(dp[i][k] > 1LL<<63-1 || dp[i][k]<0){
                        dp[i][k]= 1LL<<63-1;
                    }
//                  printf("%d %d %d =%I64d\n",i,j,k,dp[i][k]);
                }
            }
        }
        ll ans=1LL<<63 -1;
//      printf("%I64d\n",ans);
        for(int i=1;i<=n;i++){
            ans=min(ans,dp[n][i]);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

G 简单dp
好气啊。
先跑最短路,然后跑一遍dp 就好了 ,好气啊。

H
没看,据说是个简单题。

J
其实可以做的,是个爆搜,最后还剩点时间,没敲出来,真菜。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值