几道51nod上据说是提高组难度的dp题

1409 加强版贪吃蛇

听着懵逼做着傻逼。

每个格子只能经过一次,穿过上下界答案清0,不考虑穿的话就随便dp。如果要穿就是从尽可能上面的位置穿过上界,尽可能下面的位置穿过下界。

那么转移这一列之前找一下最上面最下面可以转移的位置。注意一下转移顺序什么的把细节写陈展就好了。

 1 //Achen
 2 #include<bits/stdc++.h>
 3 #define For(i,a,b) for(int i=(a);i<=(b);i++)
 4 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
 5 #define Formylove return 0
 6 const int N=1007;
 7 typedef long long LL;
 8 typedef double db;
 9 using namespace std;
10 int n,m,a[N][N];
11 LL f[N][N][2],ans;
12 
13 template<typename T> void read(T &x) {
14     char ch=getchar(); x=0; T f=1;
15     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
16     if(ch=='-') f=-1,ch=getchar();
17     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
18 }
19 
20 void GM(LL &x,LL y) { if(x<y) x=y; }
21 
22 //#define ANS
23 int main() {
24 #ifdef ANS
25     freopen("1.in","r",stdin);
26     //freopen("1.out","w",stdout);
27 #endif
28     read(n); read(m);
29     For(i,1,n) For(j,1,m) read(a[i][j]);
30     memset(f,128,sizeof(f));
31     LL inf=f[0][0][0];
32     For(i,1,n) if(a[i][1]!=-1) {
33         f[i][1][0]=f[i][1][1]=a[i][1];
34     }
35     For(j,1,m) {
36         int pos1=0,pos2=n+1;
37         For(i,1,n) {
38             if(a[i][j]==-1) break;
39             if(f[i][j][0]!=inf) { pos1=i; break; } 
40         }
41         Rep(i,n,1) {
42             if(a[i][j]==-1) break;
43             if(f[i][j][0]!=inf) { pos2=i; break; } 
44         }
45         For(i,1,n) if(f[i][j][0]!=inf&&i+1<=n&&a[i+1][j]!=-1) GM(f[i+1][j][0],f[i][j][0]+a[i+1][j]);
46         Rep(i,n,1) if(f[i][j][1]!=inf&&i-1>=1&&a[i-1][j]!=-1) GM(f[i-1][j][1],f[i][j][1]+a[i-1][j]);
47         if(pos1&&pos1!=n&&a[n][j]!=-1) {
48             int now=0;
49             GM(f[n][j][1],a[n][j]);
50             Rep(i,n-1,pos1+1) {
51                 if(a[i][j]!=-1) { now+=a[i][j]; GM(f[i][j][1],now); }
52                 else break;
53             }
54         }
55         if(pos2!=n+1&&pos2!=1&&a[1][j]!=-1) {
56             int now=0;
57             GM(f[1][j][0],a[1][j]);
58             For(i,2,pos2-1) {
59                 if(a[i][j]!=-1) { now+=a[i][j]; GM(f[i][j][0],now); }
60                 else break;
61             }
62         }
63         For(i,1,n) {
64             if(f[i][j][0]!=inf&&j+1<=m&&a[i][j+1]!=-1) {
65                 GM(f[i][j+1][0],f[i][j][0]+a[i][j+1]);
66                 GM(f[i][j+1][1],f[i][j][0]+a[i][j+1]);
67             }
68             if(f[i][j][1]!=inf&&j+1<=m&&a[i][j+1]!=-1) {
69                 GM(f[i][j+1][0],f[i][j][1]+a[i][j+1]);
70                 GM(f[i][j+1][1],f[i][j][1]+a[i][j+1]);
71             }
72         }
73     }
74     /*For(i,1,n) {
75         For(j,1,m) {
76             LL a=f[i][j][0],b=f[i][j][1];
77             if(a==inf) a=0; if(b==inf) b=0;
78             printf("%lld(%lld) ",a,b);
79         }
80         puts("");    
81     }*/
82     For(i,1,n) For(j,0,1) GM(ans,f[i][m][j]);
83     if(!ans) ans=-1;
84     printf("%lld\n",ans);
85     Formylove;
86 }
View Code

 

1780 完美序列

发现完美序列满足条件每次从当前完美序列去掉权值最大的数仍是完美序列。那么按权值从小到大往序列里放数,每次只能在两个比当前数小1的数之间或者序列前后放进一段当前数。

f[i][j][0/1/2]表示放到第i大的数,有j对连续的第i-1大数,序列前后有多少第i-1大数时的方案数。

转移用组合数转移,$\sum _i j=n$ 所以时间复杂度是n*100的

 1 //Achen
 2 #include<bits/stdc++.h>
 3 #define For(i,a,b) for(int i=(a);i<=(b);i++)
 4 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
 5 #define Formylove return 0
 6 const int N=30007,p=1000000007;
 7 typedef long long LL;
 8 typedef double db;
 9 using namespace std;
10 int n,a[N],cnt[N],mi,mx;
11 LL C[107][107],f[N][107][3],ans;
12 
13 template<typename T> void read(T &x) {
14     char ch=getchar(); x=0; T f=1;
15     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
16     if(ch=='-') f=-1,ch=getchar();
17     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
18 }
19 
20 //#define ANS
21 int main() {
22 #ifdef ANS
23     freopen("1.in","r",stdin);
24     //freopen("1.out","w",stdout);
25 #endif
26     read(n);
27     For(i,0,100) C[i][0]=1;
28     For(i,1,100) For(j,1,i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%p; 
29     For(i,1,n) {
30         read(a[i]); 
31         if(i==1) mi=a[i],mx=a[i];
32         else {
33             mi=min(a[i],mi);
34             mx=max(a[i],mx); 
35         }
36     }
37     For(i,1,n) {
38         a[i]=a[i]-mi+1;
39         if(a[i]<=n) cnt[a[i]]++;
40     }
41     n=mx-mi+1;
42     For(i,1,n) if(!cnt[i]) {
43         puts("0");
44         return 0;
45     }
46     f[1][cnt[1]-1][2]=1;
47     For(i,2,n) For(j,0,cnt[i-1]) For(k,1,cnt[i]) {
48         (f[i][cnt[i]-k][0]+=(f[i-1][j][0]+f[i-1][j][1]+f[i-1][j][2])%p*C[j][k]%p*C[cnt[i]-1][k-1]%p)%=p;
49         if(k>=1&&j+1>=k) 
50             (f[i][cnt[i]-k][1]+=(f[i-1][j][1]+f[i-1][j][2]*2)%p*C[j][k-1]%p*C[cnt[i]-1][k-1]%p)%=p;
51         if(k>=2&&j+2>=k)
52             (f[i][cnt[i]-k][2]+=f[i-1][j][2]*C[j][k-2]%p*C[cnt[i]-1][k-1]%p)%=p;
53     }
54     For(i,0,cnt[n]) ans=(ans+f[n][i][0]+f[n][i][1]+f[n][i][2])%p;
55     printf("%lld\n",ans);
56     Formylove;
57 }
View Code

 

 

1597 有限背包计数问题

按容积分为小于sqrt(n)的和大于sqrt(n)的两部分。小于根号的直接按余数分类单调队列优化多重背包地做。大于根号的因为选不到根号个,相当于没有数量限制的完全背包,用划分数的奥妙重重的方法优化。f[i][s]表示用i个数凑出s的方案数,

g[i][s]=g[i-1][s-(m+1)]+g[i-1][s-i];

也就是要么放进一个最小数,要么所有数+1。

因为发现把一种划分方法按数的大小排序,如

2 2 3 3 3 4 5 5 5 5 6 7 7 7

可以和上面两种操作的组合形成一一对应关系。

 1 //Achen
 2 #include<bits/stdc++.h>
 3 #define For(i,a,b) for(int i=(a);i<=(b);i++)
 4 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
 5 #define Formylove return 0
 6 const int N=1e5+7,p=23333333;
 7 typedef long long LL;
 8 typedef double db;
 9 using namespace std;
10 int n,m,of,og,f[2][N],g[2][N],ans;
11 
12 template<typename T> void read(T &x) {
13     char ch=getchar(); x=0; T f=1;
14     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
15     if(ch=='-') f=-1,ch=getchar();
16     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
17 }
18 
19 int mo(int x) { return x>=p?x-p:(x<0?x+p:x); }
20 
21 //#define ANS
22 int main() {
23 #ifdef ANS
24     freopen("1.in","r",stdin);
25     //freopen("1.out","w",stdout);
26 #endif
27     read(n);
28     m=sqrt(n);
29     f[of][0]=1;
30     For(i,1,m) {
31         of^=1;
32         For(w,0,i-1) {
33             int pr=0,sum=0;
34             for(int j=0;(LL)j*i+w<=n;j++) {
35                 while(j-pr>i) {
36                     sum=mo(sum-f[of^1][pr*i+w]); pr++;
37                 } 
38                 f[of][j*i+w]=mo(f[of^1][j*i+w]+sum);
39                 sum=mo(sum+f[of^1][j*i+w]);
40             }
41         }
42     }
43     g[og][0]=1;
44     ans=f[of][n];
45     For(i,1,m) {
46         og^=1; g[og][0]=0;
47         For(s,1,n) 
48             g[og][s]=mo((s-m-1>=0?g[og^1][s-(m+1)]:0)+(s-i>=0?g[og][s-i]:0));
49         For(s,0,n) 
50             ans=mo(ans+(LL)f[of][s]*g[og][n-s]%p);
51     }
52     printf("%d\n",ans);
53     Formylove;
54 }
View Code

 

1849 Clarke and package

答案就是每个物品有贡献的概率乘上价值,对每个物品单独考虑。f[i][j]表示其余的i个物品中选了j个比当前物品大的物品的概率,n^4dp即可。

 1 //Achen
 2 #include<bits/stdc++.h>
 3 #define For(i,a,b) for(int i=(a);i<=(b);i++)
 4 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
 5 #define Formylove return 0
 6 const int N=55;
 7 typedef long long LL;
 8 typedef double db;
 9 using namespace std;
10 int n,m,k[N],c[N][N];
11 db p[N][N],f[N][N],ans;
12 
13 template<typename T> void read(T &x) {
14     char ch=getchar(); x=0; T f=1;
15     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
16     if(ch=='-') f=-1,ch=getchar();
17     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
18 }
19 
20 //#define ANS
21 int main() {
22 #ifdef ANS
23     freopen("1.in","r",stdin);
24     //freopen("1.out","w",stdout);
25 #endif
26     read(n); read(m); m=min(n,m);
27     For(i,1,n) {
28         read(k[i]);
29         For(j,1,k[i]) {
30             read(c[i][j]);
31             read(p[i][j]);
32             p[i][j]/=10000.0;
33         }        
34     }
35     For(a,1,n) For(b,1,k[a]) {
36         memset(f,0,sizeof(f));
37         f[0][0]=1.0; int o=0;
38         For(i,1,n) if(i!=a) {
39             o++;
40             db p1=0,p2=0;
41             For(j,1,k[i]) {
42                 if(c[i][j]<c[a][b]||(c[i][j]==c[a][b]&&i<a)) p1+=p[i][j];
43                 else p2+=p[i][j];
44             }
45             For(j,0,o) f[o][j]=f[o-1][j]*p1+(j?f[o-1][j-1]*p2:0);
46         }
47         db tp=0;
48         For(i,0,m-1) tp+=f[o][i];
49         ans+=p[a][b]*tp*c[a][b];
50     }
51     printf("%lf\n",ans);
52     Formylove;
53 }
View Code

 

posted @ 2018-10-14 22:40  啊宸  阅读(406)  评论(0编辑  收藏  举报