几道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

 

转载于:https://www.cnblogs.com/Achenchen/p/9788509.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值