1. 程式人生 > >bzoj 1700: [Usaco2007 Jan]Problem Solving 解題 ——dp

bzoj 1700: [Usaco2007 Jan]Problem Solving 解題 ——dp

ble 比較 表示 opened play lin ans 轉移 hid

Description

過去的日子裏,農夫John的牛沒有任何題目. 可是現在他們有題目,有很多的題目. 精確地說,他們有P (1 <= P <= 300) 道題目要做. 他們還離開了農場並且象普通人一樣找到了工作. 他們的月薪是M (1 <= M <= 1000) 元. 他們的題目是一流的難題,所以他們得找幫手.幫手們不是免費的,但是他們能保證在一個月內作出任何題目.每做一道題需要兩比付款, 第一筆A_i(1 <= A_i <= M) 元在做題的那一個月初支付, 第二筆B_i元(1 <= B_i <= M)在做完後的下一個月初支付. 每一個月牛們用上一個月掙的錢來付款. 牛沒有任何存款意識, 所以每個月的節余都回拿用去買糖吃掉了. 因為題目是相互關連的,它們必須按大概順序解出. 比如,題目3必須在解題目4 之前或同一個月解出. 找出牛們做完所有題目並支付完所有款項的最短月數.

Input

* 第一行: N 和 P

* 第2...P+1行: 第i行包含A_i和B_i, 分別是做第i道題的欲先付款和完成付款.

Output

* 第一行: 牛們做完題目和付完帳目的最少月數

Sample Input

100 5
40 20
60 20
30 50
30 50
40 40
輸入解釋:
牛們的月薪是100元. 他們有5道題目要做, 預期付款分別為 40, 60, 30, 30,
40, 完成付款分別為 20,本20, 50, 50, 40.

Sample Output

6

HINT

技術分享

這道題算是道比較經典的dp吧

f[i][j] 表示寫了i道題 最後的這個月寫了j道題

f[i][j]=f[i-k][z]+1

枚舉一下i j 以及f[i-j][k]的k就可以了

註意一下初始狀態以及枚舉順序和0的轉移就可以了

技術分享
#include<cstdio>
#include<cstring>
#include<algorithm>
using std::min;
const int M=557,inf=0x3f3f3f3f;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<0||c>9){if(c==-) f=-1; c=getchar();}
    while(c>=0&&c<=
9){ans=ans*10+(c-0); c=getchar();} return ans*f; } int ans=inf,n,m,f[M][M]; int k,s1[M],s2[M]; int main(){ memset(f,0x3f,sizeof(f)); n=read(); m=read(); for(int i=1;i<=m;i++){ k=read(); s1[i]=s1[i-1]+k; k=read(); s2[i]=s2[i-1]+k; } f[0][0]=0;f[1][1]=1;f[1][0]=2; for(int k=2;k<=m;k++){ for(int i=1;i<=k;i++)if(s1[k]-s1[k-i]<=n) for(int j=0;j<=k-i;j++)if((s1[k]-s1[k-i])+(s2[k-i]-s2[k-i-j])<=n) f[k][i]=std::min(f[k][i],f[k-i][j]+1); for(int i=1;i<=m;i++)if(s2[k]-s2[k-i]<=n)f[k][0]=min(f[k][0],f[k][i]+1); } ans=f[m][0]+1; for(int i=1;i<=m;i++) if(s2[m]-s2[m-i]<=n) ans=min(ans,f[m][i]+2); printf("%d\n",ans); return 0; }
View Code

bzoj 1700: [Usaco2007 Jan]Problem Solving 解題 ——dp