Luogu_2876_[USACO07JAN]解决问题Problem Solving

题目描述

过去的日子里,农夫John的牛没有任何题目. 可是现在他们有题目,有很多的题目.

精确地说,他们有(P(1 leq P leq 300))道题目要做. 他们还离开了农场并且象普通人一样找到了工作. 他们的月薪是(M(1 leq M leq 1000)) 元.

他们的题目是一流的难题,所以他们得找帮手.帮手们不是免费的,但是他们能保证在一个月内作出任何题目.
每做一道题需要两笔付款, 第一笔(A_i(1 leq A_i leq M))元在做题的那一个月初支付, 第二笔(B_i)((1 leq B_i leq M))在做完后的下一个月
初支付. 每一个月牛们用上一个月挣的钱来付款. 牛没有任何存款意识, 所以每个月的节余都回拿用去买糖吃掉了.

因为题目是相互关连的,它们必须按顺序解出. 比如,题目3必须在解题目4之前或同一个月解出.

找出牛们做完所有题目并支付完所有款项的最短月数.

输入输出格式

输入格式

第一行: N 和 P;
第2...P+1行: 第i行包含A_i和B_i, 分别是做第i道题的欲先付款和完成付款.

输出格式

一行: 牛们做完题目和付完帐目的最少月数.

样例

INPUT

100 5
40 20
60 20
30 50
30 50
40 40

OUTPUT

6

HINT

SOLUTION

dp(应该算区间dp吧qwq)

第一眼看这题怎么乱七八糟的,为什么这个月的决策会影响到下个月的决策啊,这不是后效性吗,那怎么可能是dp啊。

于是瞎模拟乱写。于是不会。

到网上找了官方题解。。先贴个链接吧qwq
「官方题解传送门」

所以这里我用的就是官方题解的第一种解法:(O(n^3))的解法。

首先我们的(dp)数组开两维,(dp[i][j])表示完成从第(i)题到第(j)题所用的最少月份数。
然后我们就从(dp[k][i-1]space (1 leq k<i))转移而来,再就是判定一下一个月能不能做完不能就空一个月,能做完就接上直接+1,然后再所有合法的情况中取(min)值就好了。

所以这应该算区间dp吧qwq。

其实这题这么一说看上去好像真的没啥难度,但是当时看那个乱七八糟的支付规则我还真没想到直接这么转移就好了。这也算挺好的一题吧。

我的代码和官方的代码不一样qwq:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
#define Max(a,b) ((a>b)?a:b)
#define Min(a,b) ((a<b)?a:b)
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9') {if (ch=='-')f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9') {x=x*10+ch-48;ch=getchar();}
	return x*f;}
const int N=310;
int n,m,dp[N][N],fst[N],snd[N],sum1[N],sum2[N];
inline int jud(int l,int mid,int r){
	int cnt=sum2[mid-1]-sum2[l-1]+sum1[r]-sum1[mid-1];return (cnt<=m);}
inline int jud2(int l,int r){
	int cnt=sum1[r]-sum1[l-1];if (cnt>m) return 0;
	cnt=sum2[r]-sum2[l-1];if (cnt>m) return 0;
	return 1;}
int main(){
	int i,j;
	m=read();n=read();sum1[0]=0;sum2[0]=0;
	for (i=1;i<=n;++i) {fst[i]=read();sum1[i]=sum1[i-1]+fst[i];
		snd[i]=read();sum2[i]=sum2[i-1]+snd[i];}
	memset(dp,0x3f,sizeof(dp));
	for (i=1;i<=n;++i) if (jud2(1,i)) dp[1][i]=2;else break;
	for (i=2;i<=n;++i){
		for (j=i;j<=n;++j){
			if (!jud2(i,j)) continue;
			for (int k=1;k<i;++k){
				if (jud(k,i,j)) dp[i][j]=Min(dp[i][j],dp[k][i-1]+1);
				else if (jud2(k,i-1)) dp[i][j]=Min(dp[i][j],dp[k][i-1]+2);
			}
		}
	}
	int ans=2*n+1;
	for (i=1;i<=n;++i) ans=Min(ans,dp[i][n]);
	printf("%d
",ans+1);
	return 0;
}
原文地址:https://www.cnblogs.com/hkpls/p/9819709.html