zoukankan      html  css  js  c++  java
  • 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

    --------

  • 相关阅读:
    无言
    计算机网络的所有课件
    Linux 分区
    Linux 文件管理权限
    DropDownList 控件
    CssClass初步语法了解
    BulletedList用途
    BulletedList项目控件基础CSS基础
    Checkbox与foreach循环
    RadioButton控件
  • 原文地址:https://www.cnblogs.com/mjtcn/p/8486604.html
Copyright © 2011-2022 走看看