题意:给定一个自然数N,要求把N拆成若干个正整数相加的形式,整数可重复。拆分的方案数mod取余。
思路:完全背包裸题,因为整数可重复,N相当于给定的体积。
f[0]=1; for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) f[j]=(f[j]+f[j-i])%mod;
题意:
从N个人当中选出M个人作为法官。每个人都有一个ai属性和一个bi属性。现在要要求选出来的这M个人的ai之和与bi之和的差值最小,并且在差值一样的情况下要求输出ai之和与bi之和最大的方案。
思路
这个还是从N个人当中选出M个物品的问题,但是现在容量限制有了,但是每个人的价值,体积却没有。由于要求sum(ai) 与sum(bi) 之和最大,所以我们以每个人ai与bi之和作为这个人的价值。以ai与bi之差作为这个人的体积。则 以dp[N][M][k]作为状态,表示的就是从N个人当中选出M个人,这M个人的sum(ai)与sum(bi)之差为k时的总价值。但是这道题问的是方案,所以还要记录每一个状态是由哪一个状态转移而来的。所以用一个和D[i][j][k]来记录,最后在递归返回调用。这里说一个编程技巧,如果下标有负数,就设置一个偏移量,负数用偏移量减去某个数来表示,这样就把负数问题解决了。
题意:
给你一个含有N个顶点N条边的规范多变形,每条边的权值是 * '或者 ' + '。' * '代表 这条边两端顶点值相乘,' + '代表这条边两端顶点相加。第一步任意删除一条边。之后的N-1步每一次删除一条边,同时这条边的两个顶点做边上符号的运算变换成一个新的顶点。直到最后只剩下一个顶点。 现在问你最后剩下的点最大值可能是多少。
思路:区间dp,dp的思路就不说了。说几个编程的技巧。1.因为这里相当于是从这个环中任意顺序把 N个点进行合并。可以把N个点排成一条链,然后再这个链的末尾再复制一个同样的链。这里不用真的去复制一个链再后面,只要运用求余符号就可以。比如我要求i点往后m个距离的点j的具体值。则(i+m)%N就可以得到这个点的值了。这种手法经常用在环形dp要求当中。
2.如果dp的状态,某一个维度的值是有限的,比如只有0,1两个取值,这个时候就 不要用for循环去枚举了,直接dp[][][0].dp[][][1],
#include<bits/stdc++.h> using namespace std; const int inf=0x3f3f3f3f; char c[55]; int val[55],dp_max[55][55],dp_min[55][55]; int cal(char x,int a,int b) { if(x=='t') return a+b; return a*b; } int main() { int n,i,j,k,l,p1,p2,ans,MAX,MIN,sign; while(scanf("%d",&n)!=EOF) { //cin>>c[i]>>val[i]; for(int i=0;i<n;i++) cin>>c[i]>>val[i]; for(int i=0;i<n;i++) dp_max[i][i]=dp_min[i][i]=val[i]; for(int len=1;len<n;len++) { for(int i=0;i<n;i++) { MAX=-inf,MIN=inf; j=(i+len)%n; for(int k=0;k<len;k++) { p1=(i+k)%n; p2=(i+k+1)%n; MAX=max(MAX,cal(c[p2],dp_max[i][p1],dp_max[p2][j])); MAX=max(MAX,cal(c[p2],dp_min[i][p1],dp_min[p2][j])); MIN=min(MIN,cal(c[p2],dp_max[i][p1],dp_max[p2][j])); MIN=min(MIN,cal(c[p2],dp_min[i][p1],dp_min[p2][j])); } dp_max[i][j]=MAX; dp_min[i][j]=MIN; } } ans=-inf; for(int i=0;i<n;i++){ j=(i+n-1)%n; ans=max(ans,dp_max[i][j]); } cout<<ans<<"\n"; for(int i=0;i<n;i++){ j=(i+n-1)%n; if(dp_max[i][j]==ans) cout<<i+1<<" "; } cout<<"\n"; } return 0; }