《P4953 [USACO02FEB]Cow Cycling》

非常好的一个dp题。

一开始拿到的时候想的是二分去做,但是没写出来。

其实看到这个数据范围,应该想到状压,搜索,dp。

状压 和 搜索都不行,考虑dp。

dp[i][j][k],表示i头牛,领头的还剩j点体力,其余的还剩k点体力。

为什么要这样设置状态?因为这题里面特殊的地方就是领头的和其余的差异。

那么,我们就去枚举领头羊当前一分钟的速度。

首先:我们考虑无解的情况。

显然,我们所有牛都以1走的路程就是最远的,即E。

那么当E < D时,无解。其余必定有解。

dp状态的转移:

需要注意的是,我们这里的初始状态为dp[n][e][e] = 0。一开始都没消耗。

这也表明了我们dp的递推需要倒着来。

有两种情况:

1.不更换领头羊,那么此时领头羊消耗sp * sp,其余的消耗sp。

此时,dp[i][j - sp * sp][k - sp] = min(dp[i][j - sp * sp][k - sp],dp[i][j][k] + 1)。

因为是倒着递推,且我们枚举的是每分钟的速度。

2.更换领头羊,这种状态不是很好想。

dp[i - 1][k - sp * sp][k - sp] = min(dp[i - 1][k - sp * sp][k - sp],dp[i][j][k] + 1)

领头羊就是其余的k里面选一只 - sp * sp,其余的 - sp。

这里为什么变成了i - 1 ? 

因为我们这里更换领头羊时,直接把领头羊从队伍中删去了,所以羊就少了一只。

为什么要删去呢?因为领头羊的体力并不是k,所以k - sp不适用,所以减去就可以避免这个问题。

关于答案的统计:

假设每头羊都没当领头,那么他们的消耗 = 速度,那么到终点时剩余的体力为 E - D.

所以就是dp[i][j][E - D].

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 1e3 + 5;
const LL Mod = 1000000;
#define pi acos(-1)
#define INF 1e12
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    inline LL read(){
        LL x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
}
using namespace FASTIO;

int dp[25][105][105];
int main()
{
    int n,e,d;n = read(),e = read(),d = read();
    if(e < d){
        printf("0\n");
        return 0;
    }
    memset(dp,0x3f3f3f,sizeof(dp));
    dp[n][e][e] = 0;
    for(int i = n;i >= 1;--i){
        for(int j = e;j >= 0;--j){
            for(int k = e;k >= 0;--k){
                for(int sp = 1;sp <= 10;++sp){
                    if(j - sp * sp >= 0 && k - sp >= 0) dp[i][j - sp * sp][k - sp] = min(dp[i][j - sp * sp][k - sp],dp[i][j][k] + 1);//不更换领头
                    if(k - sp * sp >= 0 && k - sp >= 0) dp[i - 1][k - sp * sp][k - sp] = min(dp[i - 1][k - sp * sp][k - sp],dp[i][j][k] + 1);//更换领头
                }
            }
        }
    }
    LL ans = INF;
    for(int i = 1;i <= n;++i){
        for(int j = 0;j <= e;++j){
            ans = min(ans,1LL * dp[i][j][e - d]);
        }
    }
    printf("%lld\n",ans);
    system("pause");
    return 0;
}
View Code

 

posted @ 2021-02-10 09:18  levill  阅读(75)  评论(0编辑  收藏  举报