dp专练

dp练习。

codevs 1048 石子归并 

区间dp

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<iostream>
 6 
 7 using namespace std;
 8 
 9 int f[110][110],w[110],sum[110];
10 
11 inline int read() {
12     int x = 0,f = 1;char ch = getchar();
13     for (; ch<'0'||ch>'9'; ch = getchar()) if (ch=='-') f = -1;
14     for (; ch>='0'&&ch<='9'; ch = getchar()) x = x * 10 + ch - '0';
15     return x * f;
16 }
17 
18 int main() {
19     memset(f,0x3f,sizeof(f));
20     int n = read();
21     for (int i=1; i<=n; ++i) w[i] = read();
22     for (int i=1; i<=n; ++i) sum[i] = sum[i-1] + w[i];
23     for (int i=1; i<=n; ++i) f[i][i] = 0;
24     for (int i=n; i>=1; --i) // 枚举顺序:确保下次计算的状态中需要用到的状态已经计算出了。
25         for (int j=i+1; j<=n; ++j) 
26             for (int k=i; k<j; ++k) 
27                 f[i][j] = min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
28     printf("%d
",f[1][n]);
29     return 0;
30 }
View Code

codevs  1090 加分二叉树

区间dp

 1 #include<cstdio>
 2 
 3 int f[110][110],w[110],sum[110];
 4 int rt[110][110];
 5 
 6 inline int read() {
 7     int x = 0,f = 1;char ch = getchar();
 8     for (; ch<'0'||ch>'9'; ch = getchar()) if (ch=='-') f = -1;
 9     for (; ch>='0'&&ch<='9'; ch = getchar()) x = x * 10 + ch - '0';
10     return x * f;
11 }
12 void dfs(int l,int r) {
13     if (l > r) return ;
14     printf("%d ",rt[l][r]);
15     dfs(l,rt[l][r]-1);
16     dfs(rt[l][r]+1,r);
17 }
18 int main() {
19     int n = read();
20     for (int i=1; i<=n; ++i) w[i] = read();
21     for (int i=1; i<=n; ++i)
22             f[i][i] = w[i],rt[i][i] = i;
23     for (int i=n; i>=1; --i) 
24         for (int j=i+1; j<=n; ++j) // 中序遍历i~j的子树
25             for (int t,k=i; k<=j; ++k) { // 枚举根为k
26                 if (k==i) t = f[k+1][j] + w[k];
27                 else if (k==j) t = f[i][k-1] + w[k];
28                 else t = f[i][k-1]*f[k+1][j]+w[k];
29                 if (t > f[i][j]) f[i][j] = t,rt[i][j] = k;
30             }
31     printf("%d",f[1][n]);
32     puts("");
33     dfs(1,n);
34     return 0;
35 }
View Code

openjudge 1768 最大子矩阵

n^3求最大子矩阵

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 
 5 using namespace std;
 6 
 7 int a[110][110],s[110][110],f[110];
 8 
 9 int main () {
10     int n;
11     scanf("%d",&n);
12     for (int i=1; i<=n; ++i) 
13         for (int j=1; j<=n; ++j) 
14             scanf("%d",&a[i][j]);
15     for (int j=1; j<=n; ++j) // lie 
16         for (int i=1; i<=n; ++i) // hang
17             s[j][i] = s[j][i-1] + a[i][j];
18     int ans = 0;
19     for (int i=1; i<=n; ++i) { // hang1
20         for (int j=i; j<=n; ++j) { // hang2
21             memset(f,0,sizeof(f));
22             for (int k=1; k<=n; ++k) { // lie
23                 f[k] = max(0,f[k-1])+s[k][j]-s[k][i-1];
24                 ans = max(ans,f[k]);
25             }
26         }
27     }
28     printf("%d",ans);
29     return 0;
30 }
View Code

openjudge 8462 大盗阿福

 1 //f[i]表示到第i家店,的最大价值
 2 //f[i] = max(f[i=1...i-2]) + a[i]
 3 //第i次转移与第i+1次转移,只多了f[i-1],所以记一个变量取出最大值即可
 4 //网上的另一种转移方程,f数组的意思不变
 5 //f[i] = max(f[i-1], f[i-2]+a[i]);  
 6 #include<cstdio>
 7 #include<cstring>
 8 #include<cmath>
 9 #include<algorithm>
10 #include<iostream>
11 
12 using namespace std;
13 
14 int a[100100],f[100100];
15 
16 int main () {
17     int T,n;
18     scanf("%d",&T);
19     while (T--) {
20         scanf("%d",&n);
21         for (int i=1; i<=n; ++i) scanf("%d",&a[i]);
22         if (n==1) {printf("%d
",a[1]);continue;}
23         if (n==2) {printf("%d
",max(a[1],a[2]));continue;}
24         
25         memset(f,0,sizeof(f));
26         int mx = a[1];
27         f[1] = a[1];
28         f[2] = max(a[1],a[2]);
29         for (int i=3; i<=n; ++i) {
30             f[i] = mx + a[i];
31             mx = max(f[i-1],mx);
32         }
33         int ans = -1;
34         for (int i=1; i<=n; ++i) 
35             ans = max(ans,f[i]);
36         printf("%d
",ans);
37     }
38     return 0;
39 }
View Code

openjudge 8464 股票买卖

 1 //读懂题目,只有两次买卖 
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<iostream>
 7 
 8 using namespace std;
 9 
10 int a[100100],f1[100100],f2[100100];
11 
12 int main () {
13     int T,n;
14     scanf("%d",&T);
15     while (T--) {
16         scanf("%d",&n);
17         for (int i=1; i<=n; ++i) scanf("%d",&a[i]);
18         memset(f1,0,sizeof(f1));
19         memset(f2,0,sizeof(f2));
20         int mn = a[1];f1[1] = 0;
21         for (int i=2; i<=n; ++i) {
22             f1[i] = a[i] - mn;
23             f1[i] = max(f1[i],f1[i-1]);
24             mn = min(a[i],mn);
25         }
26         int mx = a[n];f2[n] = 0;
27         for (int i=n-1; i>=1; --i) {
28             f2[i] = mx - a[i];
29             f2[i] = max(f2[i],f2[i+1]);
30             mx = max(a[i],mx);
31         }
32         int ans = -1;
33         for (int i=2; i<n; ++i) {
34             ans = max(ans,f1[i-1]+f2[i]);
35         }
36         printf("%d
",ans);
37     }
38     return 0;
39 }
View Code

openjudge 9268 酒鬼

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<iostream>
 6 
 7 using namespace std;
 8 
 9 int a[1010],f[1010];
10 
11 int main () {
12     int n;
13     scanf("%d",&n);
14     for (int i=1; i<=n; ++i) 
15         scanf("%d",&a[i]);
16     f[1] = a[1];
17     f[2] = a[1] + a[2];
18     //当前酒可以不喝,可以喝一瓶,连续两瓶
19     for (int i=3; i<=n; ++i) 
20         f[i] = max(f[i-1],max(f[i-2]+a[i],f[i-3]+a[i-1]+a[i]));
21     printf("%d",f[n]);
22     return 0;
23 }
View Code

openjudge 7614 最低通行费

 1 //时间是2n-1,所以只能往右或往下走
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<iostream>
 7 
 8 using namespace std;
 9 
10 int a[110][110],f[110][110];
11 
12 int main () {
13     int n;
14     scanf("%d",&n);
15     for (int i=1; i<=n; ++i)
16         for (int j=1; j<=n; ++j) 
17             scanf("%d",&a[i][j]);
18     memset(f,0x3f,sizeof(f));
19     f[1][0] = f[0][1] = 0;
20     for (int i=1; i<=n; ++i) {
21         for (int j=1; j<=n; ++j) {
22             int shang = f[i-1][j],zuo = f[i][j-1],aa = a[i][j];
23             f[i][j] = min(f[i-1][j],f[i][j-1]) + a[i][j];
24             int ff = f[i][j];
25             ff = f[i][j];
26         }
27     }
28     printf("%d",f[n][n]);
29     return 0;
30 }
View Code

bzoj 2748 音量调节

 1 /*
 2 每个点可以增减一个数,求到最后一个数时的最大价值。
 3 那么有2^n个选择,2^n显然是超时的。
 4 但是n<=50, 最大音量也不超过1000,所以可以转化为判定性dp来做。
 5 到每个点f[i][j]=0/1表示到第i个点j的音量能否达到。
 6 O(n*MaxLevel) dp
 7 
 8 以空间换时间 
 9 */
10 #include<cstdio>
11 #include<cstring>
12 #include<algorithm>
13 #include<iostream>
14  
15 using namespace std;
16  
17 bool f[1010][1010];
18 int a[1010];
19  
20 inline int read() {
21     int x = 0,f = 1;char ch = getchar();
22     for (; !isdigit(ch); ch=getchar()) if(ch=='-') f=-1;
23     for (; isdigit(ch); ch=getchar()) x=x*10+ch-'0';
24     return x * f;
25 }
26  
27 int main() {
28     int n = read(),s = read(),t = read();
29     for (int i=1; i<=n; ++i) a[i] = read();
30     memset(f,false,sizeof(f));
31     f[0][s] = true;
32     for (int i=1; i<=n; ++i) {
33         for (int j=0; j<=t; ++j) {
34             if (j-a[i] >= 0) f[i][j] |= f[i-1][j-a[i]];
35             if (j+a[i] <= t) f[i][j] |= f[i-1][j+a[i]];
36         }
37     }
38     int ans=-1;
39     for (int i=1; i<=t; ++i) 
40         if (f[n][i]) ans = i;
41     cout << ans;
42     return 0;
43 }
View Code

luogu 1052 过河

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<iostream>
 5 
 6 using namespace std;
 7 
 8 int a[110],d[110],f[300100],v[300100];
 9 
10 inline int read() {
11     int x = 0,f = 1;char ch = getchar();
12     for (; !isdigit(ch); ch=getchar()) if(ch=='-') f=-1;
13     for (; isdigit(ch); ch=getchar()) x = x*10+ch-'0';
14     return x * f;
15 }
16 
17 int main() {
18     int n = read();
19     int s = read(),t = read(),m = read();
20     for (int i=1; i<=m; ++i) a[i] = read();
21     sort(a+1,a+m+1);
22     for (int i=1; i<=m; ++i) 
23         d[i] = (a[i]-a[i-1])%2520;
24     for (int i=1; i<=m; ++i) {
25         a[i] = a[i-1] + d[i];
26         v[a[i]] = 1;
27     }
28     int L = a[m];
29     memset(f,0x3f,sizeof(f));
30     f[0] = 0;
31     for (int i=1; i<=L+t; ++i) {
32         for (int j=s; j<=t; ++j) {
33             if (i-j>=0) f[i] = min(f[i],f[i-j]);
34             f[i] += v[i];
35         }
36     }
37     int ans = 1e9;
38     for (int i=L; i<=L+t; ++i) ans = min(ans,f[i]);
39     cout << ans;
40     return 0;
41 }
View Code

bzoj 4300 绝世好题

 1 /*
 2 题意:在序列a中求以子序列(不要求连续),满足相邻两位and后不为0,b_i & b_i-1 != 0
 3 f[i]表示到第i位时的最长长度。f[i] = max(f[j]+1,f[i]),a_i & a_j !=0 
 4 复杂度O(n*n),超时。 
 5  
 6 换状态表示。
 7 到第i位的最长长度换一种求法。
 8 将i化成二进制思考,考虑可以转移到i的j,j必须满足它的二进制有一位和i的二进制同为1
 9 那么可以dp[x],表示i以前第x位上为1的所有数中,最长的长度。
10 每次用dp[]数组来计算到i的最长长度,然后以i来更新。
11 复杂度O(30*n)  
12 
13 */
14 #include<cstdio>
15 #include<algorithm>
16 #include<cstring>
17 #include<iostream>
18  
19 using namespace std;
20  
21 int b[100100],f[50];
22  
23 inline int read() {
24     int x = 0,f = 1;char ch = getchar();
25     for (; !isdigit(ch); ch=getchar()) if(ch=='-') f=-1;
26     for (; isdigit(ch); ch=getchar()) x=x*10+ch-'0';
27     return x * f;
28 }
29  
30 int main() {
31     int n = read();
32     for (int i=1; i<=n; ++i) b[i] = read();
33     for (int i=1; i<=n; ++i) {
34         int mx = 0;
35         for (int j=0; j<=30; ++j) 
36             if (b[i] & (1<<j)) mx = max(mx,f[j]);
37         for (int j=0; j<=30; ++j) 
38             if (b[i] & (1<<j)) f[j] = max(f[j],mx+1);
39     }
40     int ans = 0;
41     for (int i=0; i<=30; ++i) ans = max(ans,f[i]);
42     cout << ans;
43     return 0;
44 }
View Code

bzoj 1261 zh_tree

 1 /*
 2 因为是中序遍历,中序遍历的性质:任何点都可以做根节点。
 3 所以枚举根节点即可。
 4 处理l-r区间,枚举根节点是谁。
 5 
 6 f[l][r][dep]表示l,r区间,根节点的深度是dep,构造成树的最小代价
 7 那么f[l][r][dep] = min(f[l][k][dep+1]+f[k+1][r][dep+1]+(k*(dep+1)+c)*d[i]/sum) 
 8 转移到两个深度+1的子节点中。
 9 
10 另一种方法:
11 f[l][r]表示l,r区间,构造成树的最小代价。默认根节点的位置是1
12 但是转移到的两个子节点的根节点也是1,所以对这两颗子树中的所有点的深度都加1。 
13 f[l][r] = min(f[l][k]+f[k+1][r]+(c*d[i])/sum) // 子树中的c被子树加上了。
14 f[l][r] += k*d[l]/sum + k*d[l+1]/sum +...+ k*d[r]/sum 
15 
16 
17 */
18 #include<cstdio>
19 #include<algorithm>
20 #include<cstring>
21 #include<iostream>
22 
23 using namespace std;
24 
25 const int N = 110;
26 int n;
27 double f[N][N],k,c;
28 int sum[N];
29 
30 double dfs(int l,int r) {
31     if (l > r) return 0;
32     if (f[l][r] != -1) return f[l][r];
33     double &ans = f[l][r];
34     ans = 1e18;
35     for (int i=l; i<=r; ++i) 
36         ans = min(ans,dfs(l,i-1)+dfs(i+1,r)+(1.0*c*(sum[i]-sum[i-1]))/(1.0*sum[n]));
37     ans += (1.0*k*(sum[r]-sum[l-1]))/(1.0*sum[n]);
38     return ans;
39 }
40 int main() {
41     scanf("%d%lf%lf",&n,&k,&c);
42     for (int a,i=1; i<=n; ++i) {
43         scanf("%d",&sum[i]);sum[i] += sum[i-1];
44     }
45     for (int i=1; i<=35; ++i) 
46         for (int j=1; j<=35; ++j) 
47             f[i][j] = -1;
48     printf("%.3lf",dfs(1,n));
49     return 0;
50 }
View Code

luogu P1970 花匠

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<cmath>
 6 
 7 using namespace std;
 8 
 9 const int N = 100100;
10 int a[N],f[N][2];
11 
12 inline int read() {
13     int x = 0,f = 1;char ch = getchar();
14     for (; !isdigit(ch); ch=getchar()) if(ch=='-') f=-1;
15     for (; isdigit(ch); ch=getchar()) x = x*10+ch-'0';
16     return x * f;
17 }
18 
19 int main() {
20     int n = read();
21     for (int i=1; i<=n; ++i) a[i] = read();
22     f[1][0] = f[1][1] = 1;
23     for (int i=2; i<=n; ++i) {
24         if (a[i] > a[i-1]) {
25             f[i][1] = f[i-1][0] + 1;
26             f[i][0] = f[i-1][0];
27         } else if (a[i-1] > a[i]) {
28             f[i][0] = f[i-1][1] + 1;
29             f[i][1] = f[i-1][1];
30         } else {
31             f[i][0] = f[i-1][0];
32             f[i][1] = f[i-1][1];
33         }
34     }
35     cout << max(f[n][0],f[n][1]);    
36     return 0;
37 }
View Code

贪心的思想:每次取的花都应当在上升序列和下降序列的转折点上,加上首尾的花。

bzoj 1260: [CQOI2007] 涂色paint

 1 /*
 2 区间dp,f[i][j]表示区间i到j的答案。
 3 转移时枚举中间点即可。
 4 但是此题不同于一般dp之处在于,可以首先覆盖一部分,然后在覆盖这一部分其他的地方。
 5 这样或许是可以节省次数的。
 6 
 7 AAABA 例如对于这样的例子,直接合并找中间点是无法做到最优的,3步。 
 8 而首先覆盖[1,5],然后覆盖[4,4]只需要两步。 
 9 
10 所以转移时应该特判,可以在很早时,已经可以顺便把这个点的情况。 
11 */
12 #include<cstdio>
13 #include<cstring>
14 #include<iostream>
15 
16 const int N = 55;
17 int f[N][N];
18 char s[N];
19 
20 int main() {
21     scanf("%s",s+1);
22     int n = strlen(s+1);
23     memset(f,0x3f,sizeof(f));
24     for (int i=1; i<=n; ++i) f[i][i] = 1;
25     for (int k=2; k<=n; ++k) {
26         for (int i=1; (i+k-1)<=n; ++i) {
27             int j = i + k - 1;
28             if (s[i]==s[i+1]) f[i][j] = f[i+1][j];
29             else if (s[j-1]==s[j]) f[i][j] = f[i][j-1];
30             else if (s[i]==s[j]) f[i][j] = std::min(f[i][j-1],f[i+1][j]);
31             else {
32                 for (int m=i; m<j; ++m) 
33                     f[i][j] = std::min(f[i][j],f[i][m]+f[m+1][j]);
34             }
35         }
36     }
37     std::cout<<f[1][n];
38     return 0;
39 }
View Code

bzoj 4247 挂饰

 1 /*
 2 考虑状态的表示f[i][j]表示到第i个物品,剩余j个挂钩,最大的价值。
 3 然后发现j无法转移,因为挂饰的顺序可以交换,可以在后面拿一个挂钩很多的,然后在前面选一些挂钩较少的。 
 4 那么当前j是负数也无所谓了,只要后面有一个挂钩很多的,把前面的负数补上就可以了。 
 5 
 6 如何处理,
 7 1.允许负数,f[i][j],j可以是负数。 (没写) 
 8 2.改变枚举顺序,从挂钩多的到挂钩少的枚举。(排序) 
 9 
10 如果用“拉”的写法:f[i][j] <- f[i][j-a[i]+1] (+1,表示一个挂钩挂第i个物品)
11 然后,j-a[i]+1可能小于0,那么我们只要让他从f[i-1][1]转移就好了,表示只有一个挂钩挂第i个物品。
12 
13 如果用“推”的写法:f[i][j] -> f[i+1][j+a[i]-1] ,j+a[i]-1>n的时候,强制为n即可,大于n的没有用。 
14 
15 */
16 #include<cstdio>
17 #include<algorithm>
18 #include<cstring>
19 #include<iostream>
20 #include<cctype>
21 
22 const int N = 2010;
23 
24 struct Node{
25     int a,b;
26     bool operator < (const Node &A) const {
27         return a > A.a;
28     }
29 }d[N];
30 int f[N][N];
31 
32 inline int read() {
33     int x = 0,f = 1;char ch=getchar();
34     for (; !isdigit(ch); ch=getchar()) if(ch=='-')f=-1;
35     for (; isdigit(ch); ch=getchar()) x=x*10+ch-'0';
36     return x*f;
37 }
38 int main() {
39     int n = read();
40     for (int i=1; i<=n; ++i) 
41         d[i].a = read(),d[i].b = read();
42     std::sort(d+1,d+n+1);
43     memset(f,-0x3f,sizeof(f));
44     f[0][1] = 0;
45     for (int i=1; i<=n; ++i) 
46         for (int j=0; j<=n; ++j) 
47             f[i][j] = std::max(f[i-1][j],f[i-1][std::max(j-d[i].a,0)+1]+d[i].b);
48     int ans = 0;
49     for (int i=0; i<=n; ++i) 
50         ans = std::max(ans,f[n][i]);
51     std::cout<<ans;
52     return 0;
53 }
View Code

bzoj 1296 [SCOI2009]粉刷匠

 1 /*
 2 给n个木板,一共可以刷T次,每次将可以将一段连续的格子刷成0/1 ,求最多可以刷多大的面积。
 3 
 4 n个木板是相互不干涉的,(不能跨木板刷,刚开始以为给了一个矩阵,然后以为可以竖着刷。。。) 
 5 所以对于每个木板求出刷了1,2,3...T次的最大价值。
 6 然后背包合并即可。
 7 
 8 f[i][j]表示到第i个格子,刷了j次的最大价值。
 9 dp[i]表示一共刷了i次的最大价值。 
10 */
11 #include<cstdio>
12 #include<algorithm>
13 #include<cstring>
14 #include<cmath>
15 #include<iostream>
16 #include<cctype>
17 using namespace std;
18 
19 int f[55][55],dp[2500],sum[55];
20 char s[55];
21 
22 
23 int main() {
24     int n,m,t;
25     cin >> n >> m >> t;
26     for (int T=1; T<=n; ++T) {
27         scanf("%s",s+1);
28         for (int i=1; i<=m; ++i) sum[i] = sum[i-1] + (s[i]=='1');
29                 
30         for (int i=1; i<=m; ++i) {
31             for (int j=1; j<=i; ++j) {
32                 f[i][j] = 1; // 到第i个位置,用了j次。 
33                 for (int k=0; k<i; ++k) { // 到第k个位置,用了j-1次 ,k=0表示从头开始涂。 
34                     f[i][j] = max(f[i][j],f[k][j-1]+max(sum[i]-sum[k],i-k-sum[i]+sum[k])); 
35                 }
36             }
37         }
38         
39         for (int i=t; i>=0; --i) {
40             for (int j=1,lim=min(i,m); j<=lim; ++j) 
41                 dp[i] = max(dp[i],dp[i-j]+f[m][j]);
42         }
43     }
44     
45     int ans = 0;
46     for (int i=1; i<=t; ++i) ans = max(ans,dp[i]);
47     cout << ans;
48     
49     return 0;
50 }
View Code

bzoj 1025 [SCOI2009]游戏

 1 /*
 2 思路题。
 3 首先将置换编程循环节的形式,那么答案为lcm(L1,L2,L3...Lk),Li为第i个循环节的长度。
 4 (理解:假设每个置换是一个环,那么为了让所有的环走回起点,那么走的长度就是所有环的长度的最小公倍数)
 5 
 6 那么就是求和为n的序列的最小公倍数的种数。
 7 
 8 构造一个答案(即构造最小公倍数),判断是否可行。
 9 
10 设构造的最小公倍数为A=p1^a1 * p2^a2 * p3^a3...
11 那么假设每个循环节为A1=p1^a1, A2=p2^a2, A3=p3^a3
12 因为这些循环节(Ai)之间没有共同的质因子,所以它们的lcm就是乘起来。
13 对于这些循环节长度,如果它们的和<=n那么说明对于最小公倍数x是可行的(其它的循环节补1)。 
14 
15 那么就是求有多少种(a1,a2,a3,...am),满足p1^a1+p2^a2+p3^a3+...+pm^am<=n 
16 备注:每一种(a1,a2,a3...am)对应得p1^a1+p2^2+p3^a3 都是不同的。 
17 
18 f[i][j]表示前i个质数,和为j的方案数。f[i][j] += f[i-1][j-k]  k=pri[i]^0,1,2,... 
19  
20 */
21 
22 #include<cstdio>
23 #include<iostream>
24 #define LL long long
25 #define N 1010
26 
27 LL f[N][N];
28 int pri[N],n,tot;
29 bool nopri[N];
30 
31 void getpri() {
32     for (int i=2; i<=n; ++i) {
33         if (!nopri[i]) pri[++tot] = i;
34         for (int j=1; j<=tot&&i*pri[j]<=n; ++j) { 
35             nopri[i*pri[j]] = true;
36             if (i % pri[j] == 0) break;
37         }
38     }
39 }
40 void solve() {
41     f[0][0] = 1; 
42     // f[i][j] 前i个质数,和为j的排数。
43     for (int i=1; i<=tot; ++i) {
44         for (int j=0; j<=n; ++j) {
45             f[i][j] = f[i-1][j]; // 第i个质数不用,(即0次方) 
46             for (int k=pri[i]; k<=j; k*=pri[i]) { // 第i个质数的1,2,3...次方 
47                 f[i][j] += f[i-1][j-k];
48             }
49         }
50     }
51     LL ans = 0;
52     for (int i=0; i<=n; ++i) ans += f[tot][i];
53     std::cout << ans;
54 }
55 int main() {
56     std::cin >> n;
57     getpri();
58     solve();
59     return 0;
60 }
View Code

bzoj 1055 [HAOI2008]玩具取名

 1 /*
 2 给定W,I,N,G转换为两个字母的规则。
 3 W -> IN  ...
 4 求一个最终的字符串可以由哪个字符转移而来,可能有1,2,3,4个。
 5 
 6 开始是想的是树的做法(推回去),发现连边太多。。。
 7 区间dp,f[i][j][k]=0/1 区间[l,r]能否有字符k转移而来(k=W,I,N,G)
 8 枚举左右起点,中间点,枚举转移规则,转移。
 9 
10 开始时想的是,把每一个字符可以转移到的两个字符记下来。枚举每个字符转移。即枚举f[i][j][k],k也枚举出来。
11 后来在网上发现更简单的写法,直接把所有转移的规则记录下来,枚举所有的转移规则即可。
12 
13 由于有先后的顺序(IN->W ,NI-!>W),所以所有转移规则枚举一次即可,不需要反过来在枚举一次。
14 即f[i][j][z] <- f[i][k][x] & f[k+1][j][y]
15 不需要 f[i][j][z] <- f[i][k][y] & f[k+1][j][x],而且这样是不对的。
16 
17 */
18 #include<cstdio>
19 #include<cstring>
20 #define N 210
21 
22 struct Str{    
23     int x,y,z;
24 }g[N];
25 bool f[N][N][5];
26 int c[5],p[N];
27 char s[N];
28 
29 int main() {
30     p['W'] = 1;p['I'] = 2;p['N'] = 3;p['G'] = 4;
31     scanf("%d%d%d%d",&c[1],&c[2],&c[3],&c[4]);
32     int cnt = 0;
33     for (int i=1; i<=4; ++i) {
34         for (int j=1; j<=c[i]; ++j) {
35             scanf("%s",s);
36             g[++cnt].x = p[s[0]]; g[cnt].y = p[s[1]]; g[cnt].z = i;
37         }
38     }
39     scanf("%s",s+1);
40     int n = strlen(s+1);
41     for (int i=1; i<=n; ++i) f[i][i][p[s[i]]] = true;
42     
43     for (int len=2; len<=n; ++len) {
44         for (int r,l=1; (r=l+len-1)<=n; ++l) {
45             for (int k=l; k<=r; ++k) {
46                 for (int i=1; i<=cnt; ++i) {
47                     f[l][r][g[i].z] |= (f[l][k][g[i].x] & f[k+1][r][g[i].y]); // &比&&快好多 
48                 }
49             }
50         }
51     }
52     
53     bool flag = false;
54     if (f[1][n][1]) printf("W"),flag = true;
55     if (f[1][n][2]) printf("I"),flag = true;
56     if (f[1][n][3]) printf("N"),flag = true;
57     if (f[1][n][4]) printf("G"),flag = true;
58     if (!flag) printf("The name is wrong!");
59     return 0;
60 }
View Code

bzoj 1084 [SCOI2005]最大子矩阵

 1 /*
 2 n*2的方格,选出k个矩阵。
 3 
 4 m=1时,经典dp,O(nnk)。f[i][j]到底i个位置,选了j个矩阵。转移:不选i或者枚举转移点k。 
 5 
 6 m=2时,转移时维护三维 dp[i][j][k],左边一列到第i行,右边一列到底j行,选了k个矩阵。
 7 转移:不选:可以从左边和右边转移。
 8       选:  在左边枚举转移点,在右边枚举转移点,i=j时,可以左右边一起枚举转移点。 
 9 
10 注意:转移时,不管到什么位置,必须要更新选了0个的情况(等于0),初始时更新一下行为0的情况。代码36,54行。 
11 */
12 
13 #include<cstdio>
14 #include<algorithm>
15 #include<cstring>
16 #include<iostream>
17 #include<cctype>
18 
19 using namespace std;
20 
21 const int N = 105;
22 int dp[N][N][12],sum[N][2];
23 int f[N][12],s[N];
24 int n,m,K;
25 
26 inline int read() {
27     int x = 0,f = 1;char ch=getchar();
28     for (; !isdigit(ch); ch=getchar()) if(ch=='-')f=-1;
29     for (; isdigit(ch); ch=getchar()) x=x*10+ch-'0';
30     return x*f;
31 }
32 
33 void solve1() {
34     for (int i=1; i<=n; ++i) s[i] = s[i-1] + read();
35     memset(f,-0x3f,sizeof(f));
36     f[0][0] = 0; 
37     for (int i=1; i<=n; ++i) { // 到第i个位置 
38         f[i][0] = 0;
39         for (int j=1; j<=K; ++j) { // 选了j个矩阵 
40             f[i][j] = f[i-1][j]; // 不选i 
41             for (int k=0; k<i; ++k) { // 选i,那么i在的矩阵(当前矩阵)为[k+1~i]行。 
42                 f[i][j] = max(f[i][j],f[k][j-1]+s[i]-s[k]);
43             }
44         }
45     }
46     cout << f[n][K];
47 }
48 void solve2() {
49     for (int i=1; i<=n; ++i) 
50         for (int j=1; j<=m; ++j) 
51             sum[i][j] = sum[i-1][j] + read();
52     memset(dp,-0x3f,sizeof(dp));
53     dp[0][0][0] = 0;
54     for (int i=0; i<=n; ++i) dp[i][0][0] = dp[0][i][0] = 0;
55         
56     for (int i=1; i<=n; ++i) { // 左边一列 
57         for (int j=1; j<=n; ++j) { // 右边一列 
58             dp[i][j][0] = 0;
59             for (int k=1; k<=K; ++k) { // 选了k个矩阵 
60                 dp[i][j][k] = max(dp[i-1][j][k],dp[i][j-1][k]); // 当前这个位置不选 
61                 for (int p=0; p<i; ++p) // 选i,其构成的当前矩阵是左边一列[p+1~i]行 
62                     dp[i][j][k] = max(dp[i][j][k],dp[p][j][k-1]+sum[i][1]-sum[p][1]);  
63                 for (int p=0; p<j; ++p) // 当前矩阵是右边一列[p+1~i]行 
64                     dp[i][j][k] = max(dp[i][j][k],dp[i][p][k-1]+sum[j][2]-sum[p][2]);
65                 if (i==j) {
66                     for (int p=0; p<i; ++p) // 当前矩阵是左右两列的 [p+1~i]行 
67                         dp[i][j][k] = max(dp[i][j][k],dp[p][p][k-1]+sum[i][1]-sum[p][1]+sum[j][2]-sum[p][2]);
68                 }
69             }
70         }
71     }
72     cout << dp[n][n][K]; 
73 }
74 int main() {
75     n = read(),m = read(),K = read();
76     if (m==1) solve1();
77     else solve2();
78     return 0;
79 }
80 /*
81 5 1 2
82 -9 10 2 -1 3 
83 */
View Code

bzoj 1304 [CQOI2009]叶子的染色

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cctype>
 4 
 5 using namespace std;
 6 
 7 const int INF = 1e9;
 8 const int N = 20010;
 9 int head[N],nxt[N<<1],to[N<<1],dp[N][2];
10 int n,m,Enum;
11 
12 inline int read() {
13     int x = 0,f = 1;char ch = getchar();
14     for (; !isdigit(ch); ch=getchar()) if(ch=='-') f=-1;
15     for (; isdigit(ch); ch=getchar()) x=x*10+ch-'0';
16     return x * f;
17 }
18 void add_edge(int u,int v) {
19     ++Enum; to[Enum] = v; nxt[Enum] = head[u]; head[u] = Enum;
20     ++Enum; to[Enum] = u; nxt[Enum] = head[v]; head[v] = Enum;
21 }
22 void DP(int u,int fa) {
23     if (u <= m) return ;
24     dp[u][1] = dp[u][0] = 1;
25     for (int i=head[u]; i; i=nxt[i]) {
26         int v = to[i];
27         if (v==fa) continue;
28         DP(v,u);
29         dp[u][1] += min(dp[v][1]-1,dp[v][0]);
30         dp[u][0] += min(dp[v][0]-1,dp[v][1]);
31     }
32 }
33 int main () {
34     n = read(),m = read();
35     for (int i=1; i<=m; ++i) {
36         int c = read();
37         dp[i][c] = 1;dp[i][c^1] = INF;
38     }
39     for (int i=1; i<n; ++i) {
40         int u = read(),v = read();
41         add_edge(u,v);
42     }
43     DP(n,0);
44     cout << min(dp[n][1],dp[n][0]);    
45     return 0;
46 }
View Code

bzoj 1044 [HAOI2008]木棍分割

 1 /*
 2 先二分+贪心判断。
 3 然后dp
 4 f[i][j]到第i个数,分了j次的最大价值。
 5 f[i][j] = {sigma f[k][j-1] ,sum[i]-sum[k]<=Len}
 6 后面的“滑动窗口”处理。 
 7 */
 8 #include<cstdio>
 9 #include<algorithm>
10 #include<cstring>
11 #include<cmath>
12 #include<iostream>
13 
14 using namespace std;
15 
16 const int N = 50010;
17 const int mod = 10007;
18 int a[N],f[N][2],q[N],sum[N],n,m;
19 
20 inline int read() {
21     int x = 0,f = 1;char ch = getchar();
22     for (; !isdigit(ch); ch=getchar()) if(ch=='-') f=-1;
23     for (; isdigit(ch); ch=getchar()) x=x*10+ch-'0';
24     return x * f;
25 }
26 
27 bool check(int x) {
28     int cnt = 1,sum = 0; // zz的写成sum=1... 
29     for (int i=1; i<=n; ++i) {
30         sum += a[i];
31         if (sum > x) {
32             sum = a[i];
33             cnt ++;
34             if (cnt == m + 2) return false;
35         }
36     }
37     return true;
38 }
39 int main () {
40     n = read(),m = read();
41     int L = 0,R = 0,mid,ans;
42     for (int i=1; i<=n; ++i) {
43         a[i] = read();
44         L = max(L,a[i]),R += a[i];
45         sum[i] = sum[i-1] + a[i];
46     }
47     while (L <= R) {
48         mid = (L + R) / 2;
49         if (check(mid)) ans = mid,R = mid - 1;
50         else L = mid + 1;
51     }
52     cout << ans << ' ';
53     
54     int cur = 0,Ans = 0,tot;
55     for (int i=1; i<=n; ++i) f[i][cur] = sum[i]<=ans; // 划分了0次 
56     for (int j=1; j<=m; ++j) { 
57         cur ^= 1; 
58         tot = 0,L = 1;
59         for (int i=1; i<=n; ++i) {
60             while (L < i && sum[i]-sum[L] > ans) 
61                 tot = (tot - f[L++][cur^1] + mod) % mod;
62             f[i][cur] = tot; // 到第i个位置,划分了j次。 
63             tot = (tot + f[i][cur^1]) % mod;
64         }
65         Ans = (Ans + f[n][cur]) % mod;
66     }
67     cout << Ans;    
68     return 0;
69 }
View Code

bzoj 2431 [HAOI2009]逆序对数列

 1 /*
 2 求n个数(1,2,3...n),有多少个的逆序对数为k的序列
 3 
 4 对于当前i个数j个逆序对的方案数为f[i][j]
 5 那么它可以递推到 f[i+1][j] ~ f[i+1][j+i],即计算最后一个数放的位置 
 6 复杂度n^3
 7 
 8 把上面的式子反过来,f[a][b]可以由 f[a-1][b-a+1] ~ f[a-1][b] 转移而来
 9 每次b+1,只有一个数增加一个数减去,所以记录一个类似滑动窗口的变量,O(1)转移即可。
10 复杂度n^2
11 
12 bug一堆... 
13 */
14 
15 #include<cstdio>
16 #include<iostream>
17 
18 using namespace std;
19 
20 int f[1010][1010];
21 const int mod = 10000;
22 
23 inline int read() {
24     int x = 0,f = 1;char ch = getchar();
25     for (; !isdigit(ch); ch=getchar()) if(ch=='-') f=-1;
26     for (; isdigit(ch); ch=getchar()) x=x*10+ch-'0';
27     return x * f;
28 }
29 
30 int main () {
31     int n,k;
32     cin >> n >> k;
33     f[1][0] = 1;
34     for (int i=2; i<=n; ++i) {
35         int sum = 0;
36         int limit = min((i*(i-1))/2,k);
37         
38         for (int j=0; j<=limit; ++j) {
39             sum = (sum + f[i-1][j]) % mod;
40             if (j - i >= 0) sum = (sum - f[i-1][j-i] + mod) % mod;
41             f[i][j] = sum;
42         }
43     }
44     cout << f[n][k];
45     return 0;
46 }
View Code

bzoj 2423 [HAOI2010]最长公共子序列

 1 /*
 2 1、求两个字符串的最长公共子序列,n^2
 3 2、求最长的公共子序列有多少个,在第一问的基础上再加几次转移。 
 4 */
 5 
 6 #include<bits/stdc++.h>
 7 using namespace std;
 8 typedef long long LL;
 9 
10 inline int read() { 
11     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
12     for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
13 }
14 
15 const int N = 5010;
16 const int mod = 100000000;
17 int f[2][N],g[2][N];
18 char a[N],b[N];
19 
20 int main () {
21     scanf("%s%s",a+1,b+1);
22     int A = strlen(a+1) - 1,B = strlen(b+1) - 1;
23     
24     for (int i=0; i<=B; ++i) g[0][i] = 1;g[1][0] = 1;
25     int cur = 0;
26     for (int i=1; i<=A; ++i) {
27         cur ^= 1;
28         for (int j=1; j<=B; ++j) {
29             if (a[i] == b[j]) {
30                 f[cur][j] = f[cur^1][j-1] + 1;
31                 g[cur][j] = g[cur^1][j-1];
32                 if (f[cur][j] == f[cur][j-1]) g[cur][j] = (g[cur][j] + g[cur][j-1]) % mod;
33                 if (f[cur][j] == f[cur^1][j]) g[cur][j] = (g[cur][j] + g[cur^1][j]) % mod;
34             } 
35             else {
36                 f[cur][j] = max(f[cur][j-1],f[cur^1][j]);
37                 g[cur][j] = 0;
38                 if (f[cur][j] == f[cur][j-1]) g[cur][j] = (g[cur][j] + g[cur][j-1]) % mod;
39                 if (f[cur][j] == f[cur^1][j]) g[cur][j] = (g[cur][j] + g[cur^1][j]) % mod;
40                 if (f[cur][j] == f[cur^1][j-1]) g[cur][j] = (g[cur][j] - g[cur^1][j-1] + mod) % mod;//减去重复计算的 
41             }
42         }
43     }
44     cout << f[cur][B] << "
" << g[cur][B];    
45     return 0;
46 }
View Code

bzoj 3191 [JLOI2013]卡牌游戏

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 
 5 inline int read() {
 6     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
 7     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
 8 }
 9 
10 const int N = 55;
11 int a[N];
12 double f[N][N]; // f[i][j]剩i个玩家,第j个人获胜的概率。 
13 
14 int main() {
15     int n = read(),m = read();
16     for (int i=1; i<=m; ++i) a[i] = read();
17     
18     f[1][1] = 1.0;
19     for (int i=2; i<=n; ++i) { // 人数i 
20         for (int j=1; j<=i; ++j) { // 获胜玩家j 
21             for (int k=1; k<=m; ++k) { // 卡牌k 
22                 int t = a[k] % i; if (t==0) t = i; // 出局玩家t 
23 //                if (t == j) continue;
24                 f[i][j] += f[i-1][(j-t+i)%i] / (1.0 * m); // t出局后,所有玩家编号-t,j就成了 j-t 
25             }
26         }
27     }
28     for (int i=1; i<n; ++i) 
29         printf("%.2lf%% ",f[n][i]*100.0);
30     printf("%.2lf%%",f[n][n]*100.0);
31         return 0;
32 }
View Code

baoj 1899 [Zjoi2004]Lunch 午餐

 1 /*
 2 两个窗口打饭,每个人有打饭时间与吃饭时间,将所有人分成两组分别到两个窗口,使总时间最小。
 3 
 4 f[i][j][k]到第i个人1号窗口等饭时间为j,2号窗口为k。空间开不下。
 5 sum[i]表示到i所有人的登饭时间之和。然后发现j+k=sum[i],所以f[i][j]表示到第i人1号窗口等饭时间为j的总时间。
 6 分情况转移。 
 7 */
 8 
 9 #include<bits/stdc++.h>
10 using namespace std;
11 typedef long long LL;
12 
13 inline int read() {
14     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
15     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
16 }
17 
18 const int N = 205; 
19 
20 int f[N][N*N],sum[N];
21 struct Node{
22     int a,b;
23     bool operator < (const Node &A) const {
24         return b > A.b;
25     }
26 }T[N];
27 
28 int main() {
29     
30     int n = read();
31     for (int i=1; i<=n; ++i) 
32         T[i].a = read(),T[i].b = read();
33     
34     sort(T+1,T+n+1);
35     for (int i=1; i<=n; ++i) sum[i] = sum[i-1] + T[i].a;
36     
37     memset(f,0x3f,sizeof(f));
38     int ans = f[0][0];
39     f[0][0] = 0; 
40     
41     for (int i=1; i<=n; ++i) {
42         for (int j=0; j<=sum[i-1]; ++j) { 
43             // 第i个人在二号窗口,后面的取max为都吃完的时间。二号窗口的变动体现在,第一维从i-1到了i(即从i-1转移),sum[i]也就发生了变化。 
44             f[i][j] = min(f[i][j], max(f[i-1][j], sum[i-1]-j+T[i].a+T[i].b)); 
45             // 在一号窗口 
46             f[i][j+T[i].a] = min(f[i][j+T[i].a], max(f[i-1][j], j+T[i].a+T[i].b)); 
47         }
48     }
49     for (int i=0; i<=sum[n]; ++i) ans = min(ans,f[n][i]);
50     cout << ans;
51     return 0;
52 }
View Code

bzoj 1046 [HAOI2007]上升序列 

 1 /*
 2 求出每个点往后的最长上升子序列,判断。 
 3 */
 4 #include<bits/stdc++.h>
 5 using namespace std;
 6 typedef long long LL;
 7 
 8 inline int read() {
 9     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
10     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
11 }
12 
13 int a[10010],f[10010],g[10010];
14 
15 int find(int l,int r,int x) { // 单调递减-查找第一个大于x的数 
16     int ans = 0;
17     while (l <= r) {
18         int mid = (l + r) >> 1;
19         if (g[mid] > x) ans = mid,l = mid + 1;
20         else r = mid - 1;
21     }
22     return ans;
23 }
24 int main() {
25     int n = read();
26     for (int i=1; i<=n; ++i) a[i] = read();
27     
28     int len = 1;
29     f[n] = 1;g[len] = a[n];
30     for (int i=n-1; i>=1; --i) {
31         if (a[i] < g[len]) len++,g[len] = a[i],f[i] = len;
32         else {
33             int pos = find(1,len,a[i]);
34             g[pos+1] = max(g[pos+1],a[i]); // -写成了min。。。 
35             f[i] = pos + 1;
36         }        
37     }
38     
39     int m = read(),t,last;
40     while (m--) {
41         t = read();
42         if (t > len) {puts("Impossible");continue;}
43         last = 0;
44         for (int i=1; ; ++i) {
45             if (f[i] >= t && a[i] > a[last]) {
46                 printf("%d ",a[i]);
47                 last = i;
48                 t --;
49                 if (!t) break;
50             }
51         }
52         puts("");
53     }
54     return 0;
55 }
View Code

bzoj 1820 [JSOI2010]Express Service 快递服务 

 1 /*
 2 三个辆车是没有标号的,所以在dp是,顺序是无关的。
 3 
 4 f[i][a][b][c]表示到第i个位置,三辆车分别在a,b,c是的费用。
 5 
 6 空间太大。
 7 可以滚动掉第一维,但是空间还是太大。 
 8 
 9 然后发现a,b,c中其中一个必定是q[i](收件地点)。
10 那么f[i][a][b]表示到第i个位置,三辆车分别在a,b,q[i]的最大价值。 
11 
12 */
13 #include<bits/stdc++.h>
14 using namespace std;
15 typedef long long LL;
16  
17 inline int read() {
18     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
19     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
20 }
21  
22 const int N = 1005;
23 int g[N][N],a[N],f[2][N][N];
24  
25 int main() {
26     int n = read();
27     for(int i=1; i<=n; ++i) 
28         for (int j=1; j<=n; ++j) g[i][j] = read();
29     int tot = 0;
30     while (scanf("%d",&a[++tot]) != EOF) ;
31      
32     memset(f,0x3f,sizeof(f));
33     int cur = 1;
34     f[cur][1][2] = g[3][a[1]];
35     f[cur][1][3] = g[2][a[1]];
36     f[cur][2][3] = g[1][a[1]];
37      
38     for (int i=2; i<=tot; ++i) {
39         cur ^= 1;
40         memset(f[cur],0x3f,sizeof(f[cur]));
41         for (int j=1; j<=n; ++j) {
42             for (int k=j; k<=n; ++k) { // 从j开始!减小常数! 
43                  // 当前三辆车的位置为j,k,a[i],所以从a[i-1]转移到a[i] 
44                 f[cur][j][k] = f[cur][k][j] = min(f[cur][j][k],f[cur^1][j][k]+g[a[i-1]][a[i]]); 
45                 // 当前三辆车的位置为a[i-1],k,a[i],所以枚举一个j,从j转移到a[i] 
46                 f[cur][a[i-1]][k] = f[cur][k][a[i-1]] = min(f[cur][a[i-1]][k],f[cur^1][j][k]+g[j][a[i]]);
47                 // 当前三辆车的位置为a[i-1],j,a[i],所以枚举一个k,从k转移到a[i]
48                 f[cur][a[i-1]][j] = f[cur][j][a[i-1]] = min(f[cur][a[i-1]][j],f[cur^1][j][k]+g[k][a[i]]);
49             }
50         }
51     }
52     int ans = 0x7fffffff;
53     for (int i=1; i<=n; ++i) 
54         for (int j=1; j<=n; ++j) 
55             ans = min(ans,f[cur][i][j]);
56     cout << ans;
57     return 0;
58 }
View Code

bzoj 1996 [Hnoi2010]chorus 合唱队

 1 /*
 2 
 3 想出dp的状态表示! 
 4 
 5 */
 6 
 7 #include<bits/stdc++.h>
 8 using namespace std;
 9 typedef long long LL;
10 
11 inline int read() {
12     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
13     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
14 }
15 
16 const int N = 1005;
17 const int mod = 19650827;
18 int f[N][N][2],a[N]; 
19 
20 int main() {
21     int n = read();
22     for (int i=1; i<=n; ++i) 
23         a[i] = read(),f[i][i][0] = 1;
24     for (int i=n; i>=1; --i) {
25         for (int j=i+1; j<=n; ++j) { // 区间i~j 
26             if (a[j] > a[i]) f[i][j][1] += f[i][j-1][0];
27             if (a[j] > a[j-1]) f[i][j][1] += f[i][j-1][1];
28             if (a[i] < a[j]) f[i][j][0] += f[i+1][j][1];
29             if (a[i] < a[i+1]) f[i][j][0] += f[i+1][j][0];
30             if (f[i][j][1] > mod) f[i][j][1] %= mod;
31             if (f[i][j][0] > mod) f[i][j][0] %= mod;
32         }
33     }
34     cout << (f[1][n][0] + f[1][n][1]) % mod;
35     return 0;
36 }
View Code

bzoj 2660 [Beijing wc2012]最多的方案

 1 /*
 2 预处理出所有斐波那契数,然后从大到小选出一个方案。
 3 将斐波那契数按照01串排列,第i个为1表示第i个斐波那契数选。 
 4 
 5 1 2 3 5 8 13 
 6 16 = 3 + 13         001001
 7 13 = 3 + 5 + 8      001110
 8 13 = 1 + 2 + 5 + 8  110110
 9 16 = 1 + 2 + 13     110001
10  
11 实质就是将一个高位的1的化为两个低位的1的过程,因为不能重复,所以每个位置只能有一个1,不能有多个。
12 考虑一个1最多化为多少种方案:
13 0000001 - 0000110 - 0011010 - 1101010
14 000001 - 000110 - 011010 
15 每次只能 将最左边的一个1化为两个1,而右边的1是无法化下去的,所以可以化为的方案数为:1和上一个1之间的0的个数/2
16 dp 模拟过程即可。dp[i][0/1]当前到第几位,是0还是1的方案数。 
17 
18 说明:比如0000110中的6号:
19 1、6号要化简,那么先找5号,5-3,4
20 2、此时再找4号,4号要化简,那么先找3号 3-1,2
21 ... 
22 所以6号是无法化简的。
23 */
24 #include<bits/stdc++.h>
25 using namespace std;
26 typedef long long LL;
27 
28 inline int read() {
29     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
30     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
31 }
32 
33 const int N = 105;
34 
35 LL f[N],dp[N][2],q[N];
36 
37 int main() {
38     LL n;int tot = 0;
39     cin >> n;        
40     f[1] = 1,f[2] = 2;
41     for (int i=3; i<=88; ++i) f[i] = f[i-1] + f[i-2];
42     for (int i=88; i>=1; --i) {
43         if (n >= f[i]) {
44             q[++tot] = i;n -= f[i];
45         }
46     }
47     sort(q+1,q+tot+1);
48     dp[1][1] = 1;dp[1][0] = (q[1] - 1) / 2;
49     for (int i=2; i<=tot; ++i) {
50         dp[i][1] = dp[i-1][0] + dp[i-1][1];
51         dp[i][0] = dp[i-1][0]*((q[i]-q[i-1])/2) + dp[i-1][1]*((q[i]-q[i-1]-1)/2); // 这里必须要把除以2括起来! 
52     }
53     cout << dp[tot][0] + dp[tot][1];
54     return 0;
55 }
View Code

bzoj 3612 [Heoi2014]平衡

 1 /*
 2 妙题!
 3  
 4 一个杠杆,左边长度为n,右边也是,之间也有一个点,每个点上一个橡皮,重量相同,问拿走k个有多少种方案是杠杆平衡。
 5 
 6 相当于整数划分,左边a个数构成c,右边选k-a个数,构成c。
 7 但是这里的整数划分不能取相同的数(每个位置上一个橡皮)。
 8 原整数划分方程 f[i][j]=f[i-1][j-1]+f[i-j][j],分为增加一个1的 和 所有数都加1的转移。
 9 现在转移方程  f[i][j]=f[i-j][j-1]+f[i-j][j],分为将所有数加1然后增加一个1的 和 所有数都加1的。 
10 
11 而且不能有>n的数。所以一旦有n+1了,就减去。
12 当前为i>=n+1,假设之前的包含n+1的都减去了,i=(n+1)+(i-(n+1))
13 就是说当前为i,有j个数,那么包含n+1的就是 和为(i-n-1)有j-1个数的个数,即f[i-n-][j-1] 
14 
15 */
16 #include<bits/stdc++.h>
17 using namespace std;
18 typedef long long LL;
19 
20 inline int read() {
21     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
22     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
23 }
24 
25 int f[100010][12]; // 空间n*k 
26 
27 int main() {
28     int Case = read();
29     while (Case--) {
30         int n = read(),k = read(),p = read();
31         f[0][0] = 1;
32         int W = n * k; // 这里是最大的质量,其实是n+(n-1)+(n-2)+...共k个。 
33         for (int i=1; i<=W; ++i) {
34             for (int j=1,lim=min(i,k); j<=lim; ++j) {
35                 f[i][j] = (f[i-j][j-1] + f[i-j][j]) % p;
36                 if (i > n) f[i][j] = (f[i][j] - f[i-n-1][j-1] + p) % p;
37             }
38         }
39         
40         LL ans = 0;
41         for (int i=0; i<=W; ++i) {
42             for (int j=0; j<=k; ++j) {
43                 ans = (ans + 1ll * f[i][j] * f[i][k-j]) % p;
44                 if (j < k) ans = (ans + 1ll * f[i][j] * f[i][k-j-1]) % p; // 拿0号位置的物品 
45             }
46         }
47         cout << ans << "
";
48     }
49     
50     return 0;
51 }
View Code

bzoj 4321 queue2

 1 /*
 2 
 3 开始写了一个线性的递推,由i-1个时的方案数*(i-2)得到i的方案。发现不对。。。 
 4 
 5 想不出状态的表示,于是看题解。。。
 6 f[i][j][0/1]表示推到第i个数,其中有j对数是相差1的,其中i与i-1不相邻或相邻。
 7  
 8 为什么这样表示? 
 9  
10 开始想的是,当前状态,满足条件的个数,但是这没法转移。 
11 新加入一个数i,一个状态在i-1时不满足,而i插入到不满足的数对中间,使得没有相邻的了,满足了,所以只记录满足的情况是无法转移的,也要记下不满足的情况。
12 所以正确的状态表示为记录当前有多少不满足的对数,即相差为一的。 
13 如果只用两维的话f[i][j]表示到第i个数,有j对的话。
14 那么对于i-1和i-2挨着,i插入后和i-1挨着,但是插入到它们两个之间,此时是消了一对,加了一对,如果只用两维的话这种情况判不出来。 
15 
16 之后分情况讨论i放到哪个位置,进行转移。
17  
18 */
19  
20 #include<bits/stdc++.h>
21 using namespace std;
22 
23 const int N = 1010;
24 const int mod = 7777777;
25 long long f[N][N][2];
26 
27 int main() {
28     int n;cin >> n;
29     f[1][0][0] = 1;
30     for (int i=2; i<=n; ++i) {
31         for (int j=0; j<=n; ++j) {
32             f[i][j][1] = (f[i-1][j][1] + f[i-1][j-1][1] + f[i-1][j-1][0] * 2) % mod;
33             f[i][j][0] = (f[i-1][j+1][1] * j + f[i-1][j+1][0] * (j+1)) % mod;
34             f[i][j][0] = (f[i][j][0] + f[i-1][j][1] * (i-j-1) + f[i-1][j][0] * (i-j-2)) % mod;
35         }
36     }
37     cout << f[n][0][0];
38     return 0;
39 }
View Code

bzoj 1925 [Sdoi2010]地精部落

 1 /*
 2 https://www.cnblogs.com/ciao-sora/p/5988265.html
 3 粘个网址,以后再看。 
 4 */
 5 #include<bits/stdc++.h>
 6 using namespace std;
 7 typedef long long LL;
 8  
 9 const int N = 5005;
10 int f[2][N];
11  
12 int main() {
13     int n,p;cin >> n >> p;
14     if (n==1) return !printf("1");
15      
16     int cur = 1;
17     f[1][1] = 1;
18     for (int i=2; i<=n; ++i) {
19         cur ^= 1;
20         for (int j=1; j<=n; ++j)     
21             f[cur][j] = (f[cur][j-1] + f[cur^1][i-j]) % p;
22     }
23     cout << (f[cur][n] * 2) % p;
24     return 0;
25 }
View Code

bzoj 1966 [Ahoi2005]VIRUS 病毒检测

 1 /*
 2 开始想的dp,第一个匹配到哪,第二个匹配到哪,能不能匹配上。
 3 然后发现复杂度有点大,看题解。。。就是这样。。。 
 4 */
 5 #include<bits/stdc++.h>
 6 using namespace std;
 7 typedef long long LL;
 8 
 9 inline int read() {
10     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
11     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
12 }
13 
14 const int N = 505;
15 bool f[N<<1][N];
16 char a[N<<1],b[N]; // char a[N],b[N>>1]! 
17 int c[N<<1]; // int c[N]! 
18 
19 bool solve(int n,int m) {
20     memset(f,false,sizeof(f));
21     memset(c,0x3f,sizeof(c));
22     f[0][0] = true;
23     for (int i=1; i<=n; ++i) {
24         if (a[i] != '*') {
25             for (int j=1; j<=m; ++j) {
26                 if (a[i] == '?' || a[i] == b[j]) {
27                     f[i][j] |= f[i-1][j-1];
28                     if (a[i-1] == '*' && c[i-1] < j) f[i][j] = 1;
29                 }
30             }
31         }
32         else {
33             if (i==1) f[i][0] = true;
34             for (int j=1; j<=m; ++j) {
35                 f[i][j] = (f[i-1][j] | f[i][j-1]);
36                 if (f[i][j]) c[i] = min(c[i],j);
37             }
38         }
39     }
40     if (f[n][m]) return false;
41     return true;
42 }
43 int main() {
44     scanf("%s",a+1);int n = strlen(a+1);
45     int q = read(),ans = 0;
46     while (q--) {
47         scanf("%s",b+1);
48         int m = strlen(b+1);
49         if (solve(n,m)) ans ++;
50     }
51     cout << ans;
52     return 0;
53 }
View Code

bzoj 1237 [SCOI2008]配对

 1 /*
 2 如果没有不能相同配对的限制,那么排序后对应位置相减输出即可。
 3 题目中有这样一句话:保证所有 Ai各不相同,Bi也各不相同。
 4 开始时没读出来。。。
 5 
 6 首先还是排序。 
 7 然后分情况看一下,如果A[i]!=B[i]那么直接计算答案即可。
 8 如果有连续一个相同的,那么它可以和上一个或者下一个交叉一下。
 9 如果有两个连续相同的,那么它们也可以交叉互换一下。
10 如果有三个,可以进行三个的交叉互换,有三种交换的情况。
11 如果有四个,那么可以拆成两个的。
12 如果有五个,也可以拆。
13 ...
14 所以最多可以和前后2个位置的数交换。既保证满足条件,也保证是最优的。 
15  
16 */
17 #include<bits/stdc++.h>
18 using namespace std;
19 typedef long long LL;
20 
21 inline int read() {
22     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
23     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
24 }
25 
26 const LL INF = 1e18;
27 const int N = 1e5+10;
28 LL a[N],b[N],f[N];
29 
30 #define get(a,b) (a==b?INF:abs(a-b))
31 
32 int main() {
33     int n = read();
34     for (int i=1; i<=n; ++i) 
35         a[i] = read(),b[i] = read();
36     sort(a+1,a+n+1);
37     sort(b+1,b+n+1);
38     if (n == 1 && a[1] == b[1]) return !printf("-1");
39     f[1] = get(a[1],b[1]);
40     f[2] = min(f[1]+get(a[2],b[2]),get(a[1],b[2])+get(a[2],b[1]));
41     for (int i=3; i<=n; ++i) {
42         f[i] = f[i-1] + get(a[i],b[i]);
43         f[i] = min(f[i],f[i-2]+get(a[i-1],b[i])+get(b[i-1],a[i]));
44         f[i] = min(f[i],f[i-3]+get(a[i],b[i-2])+get(a[i-1],b[i])+get(a[i-2],b[i-1]));
45         f[i] = min(f[i],f[i-3]+get(a[i],b[i-1])+get(a[i-1],b[i-2])+get(a[i-2],b[i]));
46         f[i] = min(f[i],f[i-3]+get(a[i],b[i-2])+get(a[i-1],b[i-1])+get(a[i-2],b[i]));
47     }
48     cout << f[n];
49     return 0;
50 }
View Code

bzoj 1560 [JSOI2009]火星藏宝图

 1 /*
 2 妙题!巧妙的优化! 
 3 首先如果想到的是n^2的dp,建立一个拓扑图,在拓扑图上进行dp。
 4 发现如果a->b->c进行转移,一定不如a->c转移优,即(x+y)^2 = x^2 + y^2 + 2xy > x^2 + y^2
 5 所以每个点只会从和他临近的点转移,然后就不会了。。。
 6 
 7 这个临近具体是什么呢?
 8 考虑如何一个点会成为b这样的,可以确定每一列中,如果有一个点比b还要靠下,那么b就不会即系转换以,b可以转移到这个点,这个点在转移。 
 9 那么可以维护每一列最靠下的点是哪个,对于一个点,直接从这些点中转移即可。
10 其实这些点有些也是可以被替代的。
11 
12 时间复杂度O(nm) 
13 
14 */
15 #include<bits/stdc++.h>
16 using namespace std;
17 typedef long long LL;
18  
19 inline int read() {
20     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
21     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
22 }
23 const int N = 2e5+10;
24 const int M = 1e3+10;
25 struct Node{
26     int x,y,w;
27     bool operator < (const Node &A) const {
28         if (x == A.x) return y < A.y;
29         return x < A.x;
30     }
31 }A[N];
32 int f[M],pos[M];
33  
34  
35 int main() {
36     int n = read(),m = read();
37     for (int i=1; i<=n; ++i) {
38         A[i].x = read(),A[i].y = read(),A[i].w = read();
39     }
40     sort(A+1,A+n+1);
41     f[1] = 0;pos[1] = 1;
42     for (int i=1; i<=n; ++i) {
43         int t = -0x7fffffff;
44         for (int j=1; j<=A[i].y; ++j) {
45             if (pos[j]) 
46                 t = max(t,f[j]-(A[i].x-pos[j])*(A[i].x-pos[j])-(A[i].y-j)*(A[i].y-j));
47         }
48         f[A[i].y] = t + A[i].w;
49         pos[A[i].y] = A[i].x;
50     }
51     cout << f[m];
52     return 0;
53 }
View Code

bzoj 3174 [Tjoi2013]拯救小矮人 

 1 /*
 2 贪心+dp
 3 贪心的思想,a+b比较小的先走,那么逃跑的人是排序后的一段a+b的上升序列,所以首先按照a+b排序。
 4 贪心可以确定取的顺序是最优的,但是还要确定最多能走掉多少个
 5 接下来要dp:令f[i]表示走完i个矮人之后还能取到的最大高度
 6 这样贪心的作用就出来了:按照贪心完的顺序取走矮人,可以保证最优。
 7 一开始初始化f[0]表示没有矮人走掉,f[0]=Σa[i]。
 8 然后就是枚举取到第i个矮人,可以用它来更新f[j]的情况是f[j]+hi.b>=H。
 9 这样就可以算出最大值了
10 
11 */
12 #include<bits/stdc++.h>
13 using namespace std;
14 typedef long long LL;
15 
16 inline int read() {
17     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
18     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
19 }
20 
21 const int N = 2010;
22 struct Node{
23     int a,b;
24     bool operator < (const Node &A) const {
25         return (a + b) < (A.a + A.b);
26     }
27 }h[N];
28 int f[N];
29 
30 int main() {
31     int n = read(),sum = 0;
32     for (int i=1; i<=n; ++i) 
33         h[i].a = read(),h[i].b = read(),sum += h[i].a;
34     sort(h+1,h+n+1);
35     memset(f,-1,sizeof(f));
36     f[0] = sum;
37     int H = read(),ans = 0;
38     for (int i=1; i<=n; ++i) {
39         for (int j=ans; j>=0; --j) {
40             if (f[j] + h[i].b >= H) f[j+1] = max(f[j+1],f[j]-h[i].a);
41         }
42         if (f[ans+1] >= 0) ans++;
43     }
44     cout << ans;
45     return 0;
46 }
View Code

bzoj 1855 [Scoi2010]股票交易 

 1 /*
 2 f[i][j]表示到第i天,有j股的最大价值。
 3 朴素n^3,
 4 枚举天,枚举股票,枚举买了多少股票。
 5 f[i][j] = max(f[i-1][j],f[i-w-1][j-k]-ak))  
 6 转化枚举上一天有多少股票
 7 f[i][j] = max(f[i-1][j],f[i-w-1][k]-a(j-k)) 
 8 f[i][j] = max(f[i-1][j],f[i-w-1][k]+ak-aj) 
 9 f[i-w-1][k]+ak 用单调队列优化。 
10 https://www.cnblogs.com/qt666/p/7425571.html
11 */
12 #include<bits/stdc++.h>
13 using namespace std;
14 typedef long long LL;
15 
16 inline int read() {
17     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
18     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
19 }
20 
21 const int N = 2010;
22 int f[N][N],q[N],L,R;
23 
24 int main() {
25     memset(f,-0x3f,sizeof(f));
26     int n = read(),m = read(),w = read();
27     for (int i=1; i<=n; ++i) {
28         int a = read(),b = read(),s = read(),t = read();
29         for (int j=0; j<=s; ++j) f[i][j] = -a*j; // 买入 
30         for (int j=0; j<=m; ++j) f[i][j] = max(f[i][j],f[i-1][j]); // 卖出 
31         if (i <= w) continue;
32         L = 1,R = 0;
33         for (int j=0; j<=m; ++j) { // 买入 
34             while (L <= R && q[L] < j - s) L++; // 最多买s股,保证买的合法
35             while (L <= R && f[i-w-1][q[R]]+q[R]*a <= f[i-w-1][j]+j*a) R--; // 每次取大的,保证转移的点最优 
36             q[++R] = j;
37             if (L <= R) f[i][j] = max(f[i][j],f[i-w-1][q[L]]+q[L]*a-j*a); 
38         }
39         L = 1,R = 0;
40         for (int j=m; j>=0; --j) { // 卖出 
41             while (L <= R && q[L] > j + t) L ++; // --j + b
42             while (L <= R && f[i-w-1][q[R]]+q[R]*b <= f[i-w-1][j]+j*b) R--;
43             q[++R] = j;
44             if (L <= R) f[i][j] = max(f[i][j],f[i-w-1][q[L]]+q[L]*b-j*b);
45         }
46     }
47     int ans = 0;
48     for (int i=0; i<=m; ++i) ans = max(ans,f[n][i]);
49     cout << ans;
50     return 0;
51 }
View Code

bzoj 2424 [HAOI2010]订货

 1 /*
 2  
 3 推出状态的表示和转移方程。
 4 f[i][j]表示到第i天,库存量为j(这个库存量表示今天已经卖完了剩下的)的花费。
 5  
 6 枚举上一天的库存量。 
 7     f[i][j] = f[i-1][k] + k*m + (j+u-k)*d
 8     f[i][j] = f[i-1][k] + (m-d)*k + (j+u)*d;
 9  
10 发现对于i从1~i中取最小的,
11 对于i+1从1~i+1中取最小的,
12 那么其中1~i的是重复的,所以记一个变量,每次取min即可。
13  
14 与单调队列转移不同的是:
15     每个点的转移区间是递增的。
16     i:   1~10
17     i+1: 3~12
18     i+2: 7~15
19     这样重复的计算的是两个区间重叠的部分,前面也有不满足的。
20     所以每次需要在前面弹出一些不满足的,在后面弹出一些较大的(默认从小的转移)。
21     然后加入当前的。 
22       
23       
24 */
25 #include<bits/stdc++.h>
26 using namespace std;
27 typedef long long LL;
28  
29 inline int read() {
30     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
31     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
32 }
33  
34 #define Rep(i,a,b) for (int i = (a); i <= b; ++ i)
35 const int N = 55;
36 const int M = 10005;
37 int f[N][M],U[N],d[N];
38  
39 int main() {
40     int n = read(),m = read(),S = read();
41     Rep (i,1,n) U[i] = read();
42     Rep (i,1,n) d[i] = read();
43     memset(f,0x3f,sizeof(f));
44     f[0][0] = 0;
45     Rep (i,1,n) {
46         int res = 1e9,k = 0;
47         Rep (j,0,S) {
48             while (k <= min(j+U[i],S)) { 
49                 res = min(res, f[i-1][k] + (m - d[i]) * k); k++;
50             }
51             f[i][j] = res + (j + U[i]) * d[i];
52         }
53     }
54     cout << f[n][0];
55     return 0;
56 }
View Code

bzoj 1063 [Noi2008]道路设计

 1 /*
 2 神奇树形dp。
 3 如果树是一条链,那么不便利程度的最小值0,二叉树也是0
 4 所以只有三叉树及以上才会有不便利程度。
 5 当然是三叉树的不便利程度最大(因为相同的节点,三叉树的深度更大)。
 6 然后计算出三叉树的不便利程度最大是10;
 7 而且每个点最多被一条铁路覆盖,所以每个根节点最多向儿子连两条边,(合为一条铁路) 
 8 于是状态可以表示为 f[N][j][k=0/1/2] 第i个节点,最大的不便利程度为j,向儿子连了0/1/2条边
 9 分情况转移。 
10  
11 明白状态是怎么来的,为什么这样表示状态! 
12 */
13 #include<bits/stdc++.h>
14 using namespace std;
15 typedef long long LL;
16  
17 inline int read() {
18     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
19     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
20 }
21  
22 const int N = 100010;
23  
24 int head[N],nxt[N<<1],to[N<<1],Enum;
25 LL f[N][11][3],mod; // 第i个节点,最大的不便利程度为j,向儿子连了0/1/2条边 
26  
27 void add_edge(int u,int v) {
28     ++Enum; to[Enum] = v; nxt[Enum] = head[u]; head[u] = Enum;
29 }
30 LL get(LL t) {
31     if (!t) return 0;
32     return t % mod ? t % mod : mod; // --
33 }
34 void dfs(int u,int fa) {
35     for (int i=0; i<=10; ++i) f[u][i][0] = 1;
36     for (int i=head[u]; i; i=nxt[i]) {
37         int v = to[i];
38         if (v == fa) continue;
39         dfs(v,u);
40         for (int j=0; j<=10; ++j) {
41             LL f0 = !j ? 0 : f[v][j-1][0] + f[v][j-1][1] + f[v][j-1][2];  // 不向这个儿子连边 
42             LL f1 = f[v][j][0] + f[v][j][1]; // 向这个儿子连边 
43             LL tmp = f[u][j][2] * f0 + f[u][j][1] * f1; f[u][j][2] = get(tmp); //原来2条*现在0条,原来1条*现在1条 
44             tmp = f[u][j][1] * f0 + f[u][j][0] * f1; f[u][j][1] = get(tmp); //原来1条*现在0条,原来1条*现在1条 
45             tmp = f[u][j][0] * f0; f[u][j][0] = get(tmp);//原来0条*现在0条 
46         }
47     }
48 }
49 int main() {
50     int n = read(),m = read(); mod = read();
51     for (int i=1; i<=m; ++i) {
52         int u = read(),v = read();
53         add_edge(u,v);add_edge(v,u);
54     }
55     if (m != n - 1) {
56         puts("-1
-1");return 0;  // --
57     }
58     dfs(1,0);
59     LL ans;
60     for (int i=0; i<=10; ++i) {
61         if (ans = f[1][i][0] + f[1][i][1] + f[1][i][2]) {
62             printf("%d
%d",i,ans%mod);
63             break;
64         }
65     }
66     return 0;
67 }
View Code

bzoj 1812 [Ioi2005]riv

 1 /*
 2 树形背包,然后就不会了。。
 3 首先转化成二叉树。没想到。 
 4 f[i][j][k] 表示以i为根的子树内,有j个伐木场,距离i最近的伐木场为k(k是i的祖先)
 5 */
 6 #include<bits/stdc++.h>
 7 using namespace std;
 8 typedef long long LL;
 9  
10 inline int read() {
11     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
12     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
13 }
14  
15 const int N = 205;
16 const int INF = 1e9;
17 int head[N],nxt[N<<1],to[N<<1],len[N<<1],Enum,dis[N];
18 int f[N][N][N],w[N],tr[N],ls[N],rs[N];
19  
20 void add_edge(int u,int v,int w) {
21     ++Enum; to[Enum] = v; len[Enum] = w; nxt[Enum] = head[u]; head[u] = Enum;
22 }
23 void dfs(int u,int fa) {
24     for (int i=head[u]; i; i=nxt[i]) {
25         int v = to[i];
26         if (v == fa) continue;
27         dis[v] = dis[u] + len[i];
28         dfs(v,u);
29     }
30 }
31 int dp(int i,int j,int k) {
32     if (f[i][j][k] != -1) return f[i][j][k];
33     f[i][j][k] = INF;
34     int res = 0;
35     for (int t=0; t<=j; ++t) {
36         res = 0;
37         if (ls[i]) res += dp(ls[i],t,k); // i不建伐木场 
38         if (rs[i]) res += dp(rs[i],j-t,k); // i不建伐木场 
39         f[i][j][k] = min(f[i][j][k], res+(dis[i]-dis[k])*w[i]); // 加上i到伐木场的花费。 
40         if (t < j) { 
41             res = 0;
42             if (ls[i]) res += dp(ls[i],t,i); // 建伐木场 
43             if (rs[i]) res += dp(rs[i],j-t-1,k); // 除了左儿子,其他的都是i的兄弟节点,i建伐木场对其没有影响 
44             f[i][j][k] = min(f[i][j][k],res);
45         }
46     }
47     return f[i][j][k];
48 }
49 int main() {
50     int n = read(),m = read();
51     memset(tr,-1,sizeof(tr));
52     for (int i=1; i<=n; ++i) {
53         w[i] = read();
54         int x = read(), len = read();
55         if (tr[x] == -1) ls[x] = i,tr[x] = i;
56         else rs[tr[x]] = i,tr[x] = i;
57         add_edge(x,i,len);
58     }
59     dfs(0,0);
60     memset(f,-1,sizeof(f));
61     printf("%d
",dp(0,m,0));
62     return 0;
63 }
View Code

COGS 2240 [HZOI 2016]架设电话线路

 1 /*
 2 首先可以O(n*max(h[i])^2)即O(n*100*100)的dp
 3 f[i][j]表示到i,高度为j,然后从i-1转移,转移O(100*100)
 4 考虑如何优化转移。
 5 
 6 对于f[i][j],考虑f[i-1][k],假设k<j
 7 那么f[i][j] = f[i-1][k] + c(j - k) + (j - h[i]) ^ 2
 8 f[i][j] = f[i-1][k]- ck + cj + (j - h[i]) ^ 2
 9 设设为wij = cj + (j - h[i]) ^ 2
10 f[i][j] = f[i-1][k]- ck + wij 
11 wij只与i和j有关系,对于f[i][j]是固定的,所以现在只需要找到一个k,使得f[i-1][k]-ck最小就好了。
12 
13 同理对于k>j的也要做一遍。 
14 */
15 #include<cstdio>
16 #include<algorithm>
17 #include<cstring>
18 #include<iostream>
19 #include<cctype>
20 using namespace std;
21 typedef long long LL;
22 
23 inline int read() {
24     int x = 0, f = 1; char ch = getchar(); for (; !isdigit(ch); ch=getchar()) if (ch=='-') f = -1;
25     for (; isdigit(ch); ch=getchar()) x = x * 10 + ch - '0'; return x * f;
26 }
27 
28 int f[2][105], h[100100];
29 
30 int main() {
31     freopen("phonewire.in","r",stdin);freopen("phonewire.out","w",stdout);
32     int n = read(), c = read();
33     for (int i=1; i<=n; ++i) h[i] = read();
34     int cur = 1;
35     for (int i=h[1]; i<=100; ++i) f[cur][i] = (i - h[1]) * (i - h[1]);
36     for (int i=2; i<=n; ++i) {
37         cur ^= 1;
38         memset(f[cur], 0x3f, sizeof(f[cur]));
39         for (int res=1e9,j=0; j<=100; ++j) {
40             if (j >= h[i - 1]) res = min(res, f[cur ^ 1][j] - c * j);
41             if (j >= h[i]) f[cur][j] = min(f[cur][j], res + c * j + (j - h[i]) * (j - h[i]));
42         }
43         for (int res=1e9,j=100; j>=0; --j) {
44             if (j >= h[i - 1]) res = min(res, f[cur ^ 1][j] + c * j);
45             if (j >= h[i]) f[cur][j] = min(f[cur][j], res - c * j + (j - h[i]) * (j - h[i]));
46         }
47     }
48     int ans = 1e9;
49     for (int i=0; i<=100; ++i) ans = min(ans, f[cur][i]);
50     cout << ans;
51     return 0;
52 }
View Code

--------

原文地址:https://www.cnblogs.com/mjtcn/p/8486604.html