CF1430F Realistic Gameplay (贪心+DP)

朴素做法暴力DP,O(nk)过不去。。。

 1 #include <cmath>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #define N1 2005
 6 #define ll long long 
 7 using namespace std;
 8 
 9 int n,p;
10 int l[N1],r[N1],a[N1];
11 ll f[N1][N1];
12 ll linf=0x3f3f3f3f3f3fll;
13 
14 int main()
15 {
16     freopen("a.txt","r",stdin);
17     scanf("%d%d",&n,&p);
18     for(int i=1;i<=n;i++) scanf("%d%d%d",&l[i],&r[i],&a[i]);
19     for(int i=0;i<=n;i++) for(int k=0;k<=p;k++) f[i][k]=linf;
20     f[0][p]=0; l[n+1]=r[n]+1;
21     for(int i=1;i<=n;i++)
22     {
23         for(int k=0;k<p;k++)
24         {
25             if(a[i]%p<=k)
26             {
27                 if(a[i]/p<=r[i]-l[i]) 
28                 {
29                     f[i][k-a[i]%p]=f[i-1][k]+a[i]/p;
30                     if(a[i]/p+1<=l[i+1]-l[i]) f[i][p]=min(f[i][p],f[i-1][k]+a[i]/p+1);
31                 }
32             }
33             if(a[i]%p>k)
34             {
35                 if(a[i]/p+1<=r[i]-l[i]) 
36                 {
37                     f[i][k+p-a[i]%p]=f[i-1][k]+a[i]/p+1;
38                     if(a[i]/p+2<=l[i+1]-l[i]) f[i][p]=min(f[i][p],f[i-1][k]+a[i]/p+2);
39                 }
40             }
41         }
42         if(a[i]%p)
43         {
44             if(a[i]/p<=r[i]-l[i]) 
45             {
46                 f[i][p-a[i]%p]=f[i-1][p]+a[i]/p;
47                 if(a[i]/p+1<=l[i+1]-l[i]) f[i][p]=min(f[i][p],f[i-1][p]+a[i]/p+1);
48             }
49         }else{
50             if(a[i]/p-1<=r[i]-l[i]) 
51             {
52                 f[i][0]=f[i-1][p]+a[i]/p-1;
53                 if(a[i]/p<=l[i+1]-l[i]) f[i][p]=min(f[i][p],f[i-1][p]+a[i]/p);
54             }
55         }
56         
57     }
58     ll ans=linf;
59     for(int k=0;k<=p;k++) ans=min(ans,f[n][k]*p+p-k);
60     if(ans==linf) puts("-1"); else printf("%lld
",ans);
61     return 0;
62 }
View Code

先考虑贪心这个过程

对于从i开始连续的几波,如果我们只进行不浪费子弹的换弹,我们都可以到达那些波,同时记录到达这些波换的弹夹数g[i][j],处理出这两个数组的时间是O(n),总时间O(n^2)

然后进行DP,f[i]记录答案,表示处理完前i-1波消耗的子弹数,那么f[i]可以由所有贪心到达i-1的点j转移过来,f[i]=min(f[j]+g[j][i-1]*p),注意如果i和i-1之间有间隙时间,是可以转移的

总结:贪心可以处理时间充裕的情况,而DP则用来求解必须满弹开始的情况

 1 #include <cmath>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #define N1 2005
 6 #define ll long long 
 7 using namespace std;
 8  
 9 int n,p;
10 int l[N1],r[N1],a[N1];
11 ll f[N1],g[N1][N1],Rem[N1];
12 bool ok[N1][N1];
13 ll linf=0x3f3f3f3f3f3fll;
14 int divup(int x,int y)
15 {
16     if(x%y) return x/y+1;
17     else return x/y;
18 }
19  
20 int main()
21 {
22 //    freopen("a.txt","r",stdin);
23     scanf("%d%d",&n,&p);
24     for(int i=1;i<=n;i++) scanf("%d%d%d",&l[i],&r[i],&a[i]);
25     int rem,cnt,tim,num;
26     for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) g[i][j]=linf;
27     for(int i=2;i<=n;i++) f[i]=linf;
28     for(int i=1;i<=n;i++)
29     {
30         //greedy
31         num=divup(a[i],p);
32         if(num-1<=r[i]-l[i]) //start with full 
33         {
34             g[i][i]=cnt=num, rem=num*p-a[i];
35             Rem[i]=rem;
36             if(num<=r[i]-l[i]) //supply if empty
37             {
38                 ok[i][i]=1;
39                 if(rem==0) rem=p, cnt++;
40             }
41             for(int j=i+1;j<=n;j++) //never throw away remaining bullets
42             {
43                 if(rem>a[j]){ 
44                     rem-=a[j], g[i][j]=cnt;
45                 }else{
46                     num=divup(a[j]-rem,p); //never reload between consecutive waves
47                     if(num<=r[j]-l[j]) g[i][j]=cnt=cnt+num, rem=num*p+rem-a[j];
48                     else{
49                         break;
50                     }
51                 }
52                 Rem[i]=rem;
53                 if(num+1<=r[j]-l[j])
54                 {
55                     if(rem==0) rem=p, cnt++;//supply if empty
56                     ok[i][j]=1;
57                 }
58             }
59         }else{
60             puts("-1"); return 0;
61         }
62     }
63     f[1]=0;
64     for(int i=2;i<=n;i++)
65     {
66         //DP
67         for(int j=1;j<i;j++) 
68         {
69             if(g[j][i-1]!=linf && (ok[j][i-1] || r[i-1]<l[i]  ) )
70                 f[i]=min(f[i],f[j]+g[j][i-1]*p);
71         }
72     }
73     ll ans=linf;
74     for(int i=1;i<=n;i++) 
75         if(g[i][n]<linf/2) ans=min(ans,f[i]+g[i][n]*p-Rem[i]);
76     if(ans>=linf/2) puts("-1"); else printf("%lld
",ans);
77     return 0;
78 }
79  
原文地址:https://www.cnblogs.com/guapisolo/p/13834193.html