您的位置:首页 > 其它

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

2018-03-26 20:17 351 查看

题目描述

在一个暴风雨的夜晚,农民约翰的牛棚的屋顶、门被吹飞了。 好在许多牛正在度假,所以牛棚没有住满。 剩下的牛一个紧挨着另一个被排成一行来过夜。 有些牛棚里有牛,有些没有。 所有的牛棚有相同的宽度。 自门遗失以后,农民约翰必须尽快在牛棚之前竖立起新的木板。 他的新木材供应者将会供应他任何他想要的长度,但是供应者只能提供有限数目的木板。 农民约翰想将他购买的木板总长度减到最少。 给出 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  USACO 思路 DP