【USACO】修理牛棚(水题或DP)

题目描述

在一个暴风雨的夜晚,农民约翰的牛棚的屋顶、门被吹飞了。 好在许多牛正在度假,所以牛棚没有住满。 剩下的牛一个紧挨着另一个被排成一行来过夜。 有些牛棚里有牛,有些没有。 所有的牛棚有相同的宽度。 自门遗失以后,农民约翰必须尽快在牛棚之前竖立起新的木板。 他的新木材供应者将会供应他任何他想要的长度,但是供应者只能提供有限数目的木板。 农民约翰想将他购买的木板总长度减到最少。 给出 M(1<= M<=50),可能买到的木板最大的数目;S(1<= S<=200),牛棚的总数;C(1 <= C <=S) 牛棚里牛的数目,和牛所在的牛棚的编号stall_number(1 <= stall_number <= S),计算拦住所有有牛的牛棚所需木板的最小总长度。 输出所需木板的最小总长度作为的答案。

输入

第 1 行: M , S 和 C(用空格分开) 第 2 到 C+1行: 每行包含一个整数,表示牛所占的牛棚的编号。

输出

单独的一行包含一个整数表示所需木板的最小总长度。

样例输入

4 50 18

3 

4 

6 

8 

14

15 

16 

17 

21 

25 

26 

27 

30 

31 

40 

41 

42 

43

样例输出

25

提示

题意:c个点,用m个区间去覆盖,使区间的长度和最小.


思路:m个区间最多m-1个中断区间,减去大的m-1个区间,剩下的和则最小

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>

using namespace std;

#define FOU(i,x,y) for(int i=x;i<=y;i++)
#define FOD(i,x,y) for(int i=x;i>=y;i--)
#define MEM(a,val) memset(a,val,sizeof(a))
#define PI acos(-1.0)

const double EXP = 1e-9;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const ll MINF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const int N = 1e6+5;

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    std::ios::sync_with_stdio(false);
    int m,s,c;
    int a[205];
    int b[205];
    int vis[505];
    while(~scanf("%d%d%d",&m,&s,&c))
    {
        MEM(vis,0);
        for(int i=1;i<=c;i++)
            scanf("%d",&a[i]);
        sort(a+1,a+1+c);
        b[1]=0;
        for(int i=2;i<=c;i++)
            b[i]=a[i]-a[i-1]-1;
        sort(b+1,b+c+1);
        int ans=s;
        //首尾减去
        ans-=(a[1]-1);
        ans-=(s-a[c]);
        for(int i=c;i>=1&&i>=(c-m+2);i--)
            ans-=b[i];
        printf("%d\n",ans);
    }
    return 0;
}



第二种做法:DP,考虑转态转移,第i块板可以接在上一块板上或者新开一块板,即

dp[i][j];  //第一维表示哪个点在哪儿,第二维表示用了几块板

//新加一块板

dp[i][k+1]=min(dp[i][k],dp[j][k]+a[i]-a[j+1]+1);
//接在前一块板上

dp[i][k]=min(dp[i][k],dp[j][k]+a[i]-a[j]);

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>

using namespace std;

#define FOU(i,x,y) for(int i=x;i<=y;i++)
#define FOD(i,x,y) for(int i=x;i>=y;i--)
#define MEM(a,val) memset(a,val,sizeof(a))
#define PI acos(-1.0)

const double EXP = 1e-9;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const ll MINF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const int N = 1e6+5;

int dp[205][55];  //第一维表示哪个点在哪儿,第二维表示用了几块板

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    std::ios::sync_with_stdio(false);
    int m,s,c;
    int a[205];
    int b[205];
    int vis[505];
    while(~scanf("%d%d%d",&m,&s,&c))
    {
        MEM(vis,0);
        for(int i=1;i<=c;i++)
            scanf("%d",&a[i]);
        sort(a+1,a+1+c);
        for(int i=1;i<=c;i++)
            for(int j=1;j<=m;j++)
                dp[i][j]=INF;
        dp[1][1]=1;
        for(int i=2;i<=c;i++)
        {
            for(int j=1;j<i;j++)  //从i的前j个点转移过来
            {
                for(int k=1;k<=m;k++)
                {
                    if(k+1<=m)  //新加一块板
                        dp[i][k+1]=min(dp[i][k],dp[j][k]+a[i]-a[j+1]+1);
                    //接在前一块板上
                    dp[i][k]=min(dp[i][k],dp[j][k]+a[i]-a[j]);
                }
            }
        }
        int ans=INF;
        for(int i=1;i<=m;i++)
            ans=min(ans,dp[c][i]);
        printf("%d\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值