DP一下,马上出发

简单DP

i.May I ask you for a dance(体舞课软广植入)

这题的状态转移方程为:dp[i][j]=max(dp[i-1][j-1]+a[i][j],dp[i][j-1]);(i<=j)

方法1:预处理:将i==j的情况提取出来,因为当i==j的时候,序列顺序是固定的,只能从1选到i(感谢zzdl)

方法2:将i>=j的情况全部赋值为-inf,这样在选取的时候就能够保证i>=j不会被选取

方法3:好感度可能为负值,范围为-100~100;因为dp初始化为0,所以可能会丢失好感度为负数的情况,所以预处理的时候将好感度+100,全部变成正数进行讨论

 1 #include
 2 using namespace std;
 3 
 4 int a[120][120], dp[120][120];
 5 int n, m;
 6 const int inf = 9999999999;
 7 int main(){
 8     while(scanf("%d%d",&n,&m)!=EOF){
 9         memset(a, 0, sizeof(a));
10         for (int i = 1; i <= n;i++)
11             for (int j = 1; j <= m;j++){
12                 scanf("%d", &a[i][j]);
13                //方法3:a[i][j]+=100;
14             }
15         memset(dp, 0, sizeof(dp));
16         //3选1
17         //方法1:
18         //for (int i = 1; i <= n;i++){
19         //    for (int j = 1; j <= i;j++){
20         //        dp[i][i] += a[j][j];
21         //    }
22         //}
23         //方法2:
24         for (int i = 1; i <= n;i++)
25             for (int j = 0; j <= i;j++)
26                 dp[i][j] = -inf;
27                 
28         for (int i = 1; i <= n; i++)
29             for (int j = 1; j <= m; j++)
30                 if(i<=j)//这句可以不加
31                     dp[i][j] = max(dp[i - 1][j - 1] + a[i][j], dp[i][j - 1]);
32 
33         int ans = dp[n][1];
34         for (int j = 2; j <= m;j++)
35             ans = max(ans, dp[n][j]);
36         //方法3:ans-=100*n;
37         printf("%d\n", ans);
38     }
39     system("pause");
40     return 0;
41 }

 ii.秘密交易

题意:给定一串由“D”“G”组成的序列

发牌权首先掌握在G手中,每次的决策为:

1.一个人帮另一个人出一张牌,代价为1

2.掌控权转移,代价为1

有掌控权的人可以连续出牌不计代价

这题可以贪心,但我WA了

用DP写,dp[i][0]表示第i次掌控权在D手中,dp[i][1]表示第i次掌控权在G手上

 1 #include
 2 using namespace std;
 3 int dp[1000020][2];
 4 int main(){
 5     int t;cin>>t;
 6     while(t--){
 7         int ans = 0;
 8         char s[1000020];
 9         scanf("%s", s + 2);
10         int n = strlen(s+2);
11         if(s[n+1]=='D'&&s[n+1]=='G')
12             ans--;
13         s[1] = 'G';
14         s[0] = 'G';//可省略
15         s[n+2] = 'G';
16         s[n+3] = 'G';//可省略
17         memset(dp, 0x3f, sizeof(dp));
18         dp[0][1]=0;
19         dp[0][0] = 1;
20         for (int i = 1; i <= n + 3;i++){
21             if(s[i]=='D'){
22                 dp[i][0] = min(dp[i - 1][0], dp[i - 1][1] + 1);
23                 dp[i][1] = min(dp[i - 1][1]+1, dp[i - 1][0]+1);
24             }
25             if(s[i]=='G'){
26                 dp[i][1] = min(dp[i - 1][1], dp[i - 1][0] + 1);
27                 dp[i][0] = min(dp[i - 1][1] + 1, dp[i - 1][0]+1);
28             }
29         }
30         cout << min(dp[n + 3][0], dp[n + 3][1]) << endl;
31     }
32     system("pause");
33     return 0;
34 }

iii.HDU1024

题意:给定长度为n的序列,找出m个子序列,使总和最大

选择第i个数,使它存在于第j个子序列当中

存在于第j个子序列:

  情况1:作为第j个子序列的第一个元素

  情况2:作为第j个子序列中间的/最后的元素

则状态转移方程为:

  dp[i][j]=max(dp[i-1][j],max(dp[k][j-1]])+a[i];(1<=k<=i-1)

 这个式子的后面这一部分表示:

  选定了j-1个序列,它们的最大值,在i-1位置之前的最大值

  我个人认为这个k的范围应该是:j-1<=k<=i-1

那么可以降为:

  dp[j]=max(dp[j-1],pre[j-1])+a[j];

注意pre的求取方法

pre表示的是选取i-1段的最大值,更新以后变成选取i段的最大值。

所以只可以更新到第pre[j-1]而不是pre[j],否则在dp[j+1]的更新时会用到pre[j],而此时的pre[j]表示的是选取i段的了

 

 1 #include
 2 using namespace std;
 3 #define sys system("pause")
 4 const int maxn = 1e6 + 100;
 5 const int inf = 0x3f3f3f3f;
 6 int a[maxn], pre[maxn], dp[maxn],m,n,ans;
 7 int32_t main(){
 8     while(scanf("%d%d",&m,&n)!=EOF){
 9         for(int i=1;i<=n;i++){
10             scanf("%d", &a[i]);
11             dp[i] = pre[i] = 0;
12         }
13         dp[0] = 0;
14         pre[0] = 0;
15         for(int i=1;i<=m;i++){
16             ans = -inf;
17             for (int j = i; j <= n;j++){
18                 dp[j] = max(dp[j - 1] + a[j], pre[j-1]+a[j]);
19                 pre[j - 1] = ans;
20                 ans = max(ans, dp[j]);
21             }
22         }
23         printf("%d\n", ans);
24     }
25     //sys;
26     return 0;
27 }

 

iiii.CF Ayoub and Lost Array

DP一下,马上出发_第1张图片

题意:

给定一个闭区间,[l,r],要在里面选n个数,保证n个数的和为3的倍数

思路:数论+dp

关于数论部分:

    要让n个数的和为3,比如现在有两个数,a和b
    有一个简单的前置技能的式子,那就是:
            ((a mod n)+(b mod n) )mod n==(a+b)mod n; 
    减法和乘法同理
    那么能够满足a+b=3*k,即(a+b)%3==0 也就是 [(a%3)+(b%3) ]%3==0 如果a%3==1,那么b%3为2; 如果a%3==0,那么b%3为0; 如果a%3==2,那么b%3为1 这样才能保证两个数相加能够被3整除

关于dp部分

    这里的dp我们用一个二维数组表示,dp[N][3]; 对于dp[i][j]; i代表目前已经选择了i个数相加,j表示他们的和的余数目前是0或1或2; dp数组的值代表选择i个数相加,并且它们的和的余数为0/1/2有多少种; 在for循环里,每一次i++,我们都要选取一个新的数加进我们选择的数组,最后直到我们已经选完了n个数; 那么每次选的时候,比如说我们现在是 dp[i][2] = ( (dp[i - 1][0] * n2)%mod + (dp[i - 1][1] * n1)%mod + (dp[i - 1][2] * n0)%mod )%mod; 对于这个式子,那么就是我们现在已经有i-1个数已经排列好了,并且之前有多少种可能性已经计算过了,现在想再加一个数进去,构成一个长度为i的,和的余数为2的数组; 现在我们想在原本的i-1序列中加一个数,使得和的余数为2, 那么对于dp[i-1][0],要乘上余数为2的数的数量,也就是dp[i-1][0]*n2,这样才可以得到一个余数为2的数
 1 #include
 2 using namespace std;
 3 typedef long long ll;
 4 //#define int long long
 5 const ll N = 2e5 + 1000;
 6 const  ll mod = 1e9 + 7;
 7 ll n, dp[N][3],l,r,n0,n1,n2;
 8 int main(){
 9     scanf("%ld%ld%ld", &n,&l,&r);
10     n0= n1 = n2 = (r - l + 1) / 3;
11     ll num = (r - l + 1) % 3;
12       if(num==1){
13         if(r%3==2)
14             n2++;
15         else if(r%3==1)
16             n1++;
17         else if(r%3==0)
18             n0++;
19     }
20     else if(num==2){
21         if(r%3==1){
22             n0++;
23             n1++;
24         }
25         else if(r%3==2){
26             n1++;
27             n2++;
28         }
29         else if(r%3==0){
30             n0++;
31             n2++;
32         }
33     }
34     dp[1][0] = n0;
35     dp[1][1] = n1;
36     dp[1][2] = n2;
37     for (ll i = 2; i <= n; i++){
38         dp[i][0] = ( (dp[i - 1][0] * n0)%mod + (dp[i - 1][1] * n2)%mod + (dp[i - 1][2] * n1)%mod )%mod;
39         dp[i][1] = ( (dp[i - 1][0] * n1)%mod + (dp[i - 1][1] * n0)%mod + (dp[i - 1][2] * n2)%mod )%mod;
40         dp[i][2] = ( (dp[i - 1][0] * n2)%mod + (dp[i - 1][1] * n1)%mod + (dp[i - 1][2] * n0)%mod )%mod;
41     }
42     cout << dp[n][0];
43     //system("pause");
44     return 0;
45 }

 iiiii.codeforces429B working out

题目描述

夏天要到了,小西和小瓜决定去健身。健身房是一个n行m列的矩阵。 a[i][j]代表在第i行第j列,可以消耗的卡路里。 小西从第1行第1列出发,要到(n,m);小西可以从a[i][j],走到a[i+1][j],或者a[i][j+1]; 小瓜从(n,1)出发,要到(1,m);小瓜可以从a[i][j]出发,走到a[i-1][j],或者a[i][j+1]; 由于小西和小瓜要聚在一起发张自拍,他们必须在健身房的某一行,某一列相遇。由于他们没有好好健身,所以在自拍地点的卡路里消耗不计入总卡路里消耗值。 如果小西和小瓜中任何一个人完成了健身,则健身结束。你的任务是求出小西和小瓜可以消耗的最大总卡路里值。 另外,他们的健身速度不同,所以可以走过的路线长度也不同。

输入输出格式

输入格式:

第一行包含两个整数,n,m(3<=n,m<=1000),代表健身房的行、列数,接下来的i行j列代表a[i][j],即在第i行,j列可以燃烧的卡路里。

输出格式:

输出一个整数:即他们可能消耗的总卡路里的最大值。

说明

对于样例1,小西可以选择: a[1][1]→a[1][2]→a[2][2]→a[3][2]→a[3][3] . 小瓜可以选择: a[3][1]→a[2][1]→a[2][2]→a[2][3]→a[1][3] .

 

预处理:

定义一个三维数组,dp[i][x][y]

表示在(x,y)处,分别向(1,1),(1,m),(n,1),(n,m)点可以消耗的最大热量

i=1,2,3,4

注意预处理时循环的顺序。

取最大值:

假设第i,j点是小西和小瓜相遇的点,求最大值

注意循环的顺序

 1 #include
 2 using namespace std;
 3 #define int long long
 4 int a[1024][1024], dp[5][1024][1024], sum, n, m;
 5 int32_t main(){
 6     int n, m;
 7     scanf("%I64d%I64d", &n, &m);
 8     for(int i=1;i<=n;i++)
 9         for(int j=1;j<=m;j++)
10             scanf("%I64d", &a[i][j]);
11     for(int i=1;i<=n;i++){
12         for(int j=1;j<=m;j++)
13             dp[1][i][j] = max(dp[1][i - 1][j], dp[1][i][j - 1]) + a[i][j];
14         for(int j=m;j>=1;j--)
15             dp[3][i][j] = max(dp[3][i - 1][j], dp[3][i][j + 1]) + a[i][j];
16     }
17     for(int i=n;i>=1;i--){
18         for(int j=1;j)
19             dp[2][i][j] = max(dp[2][i + 1][j], dp[2][i][j - 1]) + a[i][j];
20         for(int j=m;j>=1;j--)
21             dp[4][i][j] = max(dp[4][i + 1][j], dp[4][i][j + 1]) + a[i][j];
22     }
23     for(int i=2;i)
24         for(int j=2;j){
25             sum = max(sum, dp[1][i - 1][j] + dp[4][i + 1][j] + dp[2][i][j - 1] + dp[3][i][j + 1]);
26             sum = max(sum, dp[1][i][j - 1] + dp[4][i][j + 1] + dp[2][i + 1][j] + dp[3][i - 1][j]);
27         }
28     printf("%I64d\n", sum);
29     //system("pause");
30     return 0;
31 }

 

6.POJ2479Maximum sum/POJ2593Max Sequence

在一个序列里取两个最大连续子序列

 1 #include
 2 #include
 3 #include
 4 using namespace std;
 5 int dp[50020], a[50020];
 6 int n, T, sum, t, ans;
 7 int main(){
 8     int T;
 9     cin >> T;
10     while(T--){
11         cin >> n;
12         memset(dp, 0, sizeof(dp));
13         for (int i = 1; i <= n; i++)
14             scanf("%d", &a[i]);
15         sum = 0, t = -99999999, ans = -99999999;
16         for (int i = 1; i <= n;i++){
17             sum += a[i];
18             t = max(sum, t);
19             dp[i] = t;
20             if(sum<0)
21                 sum = 0;
22         }
23         sum = 0, t = -9999999;
24         for (int i = n; i > 1;i--){
25             sum += a[i];
26             t = max(sum, t);
27             ans = max(ans, t + dp[i - 1]);
28             if(sum<0)
29                 sum = 0;
30         }
31         printf("%d\n", ans);
32     }
33     //system("pause");
34     return 0;
35 }

 7.牛客练习赛35背单词

记忆状态即可

 1 #include
 2 #define int long long
 3 #define scan(n) scanf("%lld",&n)
 4 #define sys system("pause")
 5 #define prin(n) printf("%lld\n",n)
 6 #define scann(n,m) scanf("%lld%lld",&n,&m)
 7 #define scannn(n,m,k) scanf("%lld%lld%lld",&n,&m,&k)
 8 #define mem(a,b) memset(a,b,sizeof(a))
 9 #define guagua 0
10 #define pb push_back
11 #define mp make_pair
12 #define ro(i, a, b) for (int i = a; i >= b;i--)
13 #define fo(i, a, b) for (int i = a; i <= b;i++)
14 #define trimax(a,b,c) max(max(a,b),c)
15 #define guagua
16 #ifdef guagua
17     #define dbg(args...) do {cout << #args << " : "; err(args);} while (0)
18 #else
19     #define dbg(...)
20 #endif
21 void err() {std::cout << std::endl;}
22 template
23 void err(T a, Args...args){std::cout << a << ' '; err(args...);}
24 using namespace std;
25 const int maxn = 3e5 + 100;
26 const int mod=1e9+7;
27 const int inf = 0x3f3f3f3f;
28 int ans[5020][3];
29 int32_t main(){
30     int T;scan(T);
31     while(T--){
32         int n,a,b;scannn(n,a,b);
33         memset(ans,0,sizeof(ans));
34         int a1=1,b1=1;
35         int k=a+1;while(k--){a1*=5;a1%=mod;}
36         k=b+1;while(k--){b1*=21;b1%=mod;}
37         ans[1][0]=5,ans[1][1]=21,ans[0][1]=1,ans[0][0]=1;
38         fo(i,2,n){
39             if(i>a){ans[i][0]=((ans[i-1][1]+ans[i-1][0])*5)%mod-(ans[i-a-1][1]*a1%mod);
40                     while(ans[i][0]<0)ans[i][0]+=mod;}
41             else ans[i][0]=((ans[i-1][0]+ans[i-1][1])*5)%mod;
42             if(i>b){ans[i][1]=(ans[i-1][1]+ans[i-1][0])*21%mod-(ans[i-b-1][0]*b1%mod);
43                     while(ans[i][1]<0)ans[i][1]+=mod;}
44             else ans[i][1]=((ans[i-1][1]+ans[i-1][0])*21)%mod;
45         }
46         int sum=0;fo(i,1,n)fo(j,0,1){sum+=ans[i][j];sum%=mod;}
47         prin(sum);
48     }
49     //sys;
50     return 0;
51 }

 

8.牛客练习赛36 rabbit的工作1

dp[j][k] 表示在i循环内,工作了j天,在第i天,已经连续工作k天(则体力会减去k),保证体力有最大值

 1 #include
 2 #define int long long
 3 #define scan(n) scanf("%lld",&n)
 4 #define sys system("pause")
 5 #define prin(n) printf("%lld",n)
 6 #define scann(n,m) scanf("%lld%lld",&n,&m)
 7 #define scannn(n,m,k) scanf("%lld%lld%lld",&n,&m,&k)
 8 #define mem(a,b) memset(a,b,sizeof(a))
 9 #define guagua 0
10 #define pb push_back
11 #define mp make_pair
12 #define ro(i, a, b) for (int i = a; i >= b;i--)
13 #define fo(i, a, b) for (int i = a; i <= b;i++)
14 #define trimax(a,b,c) max(max(a,b),c)
15 #define guagua
16 #ifdef guagua
17     #define dbg(args...) do {cout << #args << " : "; err(args);} while (0)
18 #else
19     #define dbg(...)
20 #endif
21 void err() {std::cout << std::endl;}
22 template
23 void err(T a, Args...args){std::cout << a << ' '; err(args...);}
24 using namespace std;
25 const int maxn = 3e5 + 100;
26 const int inf = 0x3f3f3f3f;
27 int dp[404][404];//第i天,一共工作j天,连续工作k天,存放剩余最大体力
28 int a[404];
29 int32_t main(){
30     int n,kk;scann(n,kk);
31     fo(i,1,n)scanf("%1lld",&a[i]);
32     memset(dp,-1,sizeof(dp));
33     dp[0][0]=kk;
34     int day=0;
35     fo(i,1,n){
36         if(a[i]==1){
37             day++;
38             ro(j,i,1)fo(k,1,j){
39             dp[j][0]=max(dp[j][0],dp[j][k]);
40             dp[j][k]=max(dp[j][k],dp[j-1][k-1]-k);
41         }}
42         else{
43             ro(j,i,1)fo(k,1,j){
44             dp[j][0]=max(dp[j][0],dp[j][k]);
45             }
46         }
47     }
48     int ans=0;
49     fo(j,1,n)fo(k,0,n){if(dp[j][k]>=0)ans=j;}
50     prin(ans);
51     //sys;
52     return 0;
53 }

 

状压DP

i.HDU1074

DP一下,马上出发_第2张图片

 1 #include
 2 #include
 3 #include
 4 #include
 5 using namespace std;
 6 #define int long long
 7 #define sys system("pause")
 8 #define scan(n) scanf("%lld", &(n))
 9 #define scann(n, m) scanf("%lld%lld", &(n), &(m))
10 #define scannn(a,b,c) scanf("%lld%lld%lld",&(a),&(b),&(c))
11 #define prin(n) printf("%lld", (n))
12 #define prins(n) printf("%s\n", (n))
13 #define ff first
14 #define ss second
15 #define mp make_pair
16 #define pb push_back
17 #define pii pair
18 #define mem(a) memset(a,0,sizeof(a))
19 #define fo(a, b) for (int i = (a); i <= (b);i++)
20 #define REP(i,n) for(int i = 1; i <= (n); i++)
21 const int maxn = (1 << 16);
22 int n, tim[maxn], dp[maxn], pre[maxn], c; //c==cost,tim数组表示状态index时所在的时间
23 char s[20][120];
24 struct node{
25     int d;//deadline
26     int c;//cost
27 } a[16];
28 void print(int x){
29     if(!x)
30         return;
31     print(x - (1 << pre[x]));
32     cout << s[pre[x]] << endl;
33 }
34 int32_t main(){
35     int t;
36     scan(t);
37     while(t--){
38         scan(n);        
39         fo(0,n-1)
40             scanf("%s %d %d", &s[i], &a[i].d, &a[i].c);
41         memset(dp, 0x3f, sizeof(dp));//刚开始初始化为无穷,为了求最小值
42         memset(pre, 0, sizeof(pre));
43         int S = (1 << n)-1;//所有状态表示为S
44         dp[0] = 0;
45         for (int i = 1; i <= S;i++){
46             for (int j = n-1; j >=0;j--){
47                 if(!(i&(1<<j)))
48                     continue;
49                 int k = i - (1 << j);//把j状态删掉,k比i小
50                 c = tim[k] + a[j].c - a[j].d;
51                 if(c<0)
52                     c = 0;//dp里面存的是花费的价值,我们需要的是最小值
53                 if(dp[k]+c//说明选择k状态
54                     dp[i] = dp[k] + c;
55                     pre[i] = j;//pre里面保存的是单个点,用于输出
56                     tim[i] = tim[k] + a[j].c;
57                 }                
58             }
59         }
60         cout << dp[S] << endl;
61         print(S);
62     }
63     sys;
64     return 0;
65 }

总状态个数为S=(1<

注意:<<运算符的优先级小于==

子集枚举

枚举所有点对:

1 for(int i=0;i)
2      for(int S=0;S<(1<){
3          d[i][S]=INF;
4          for(int j=0;j)
5              if(S&(1<<j))
6                  dp[i][S]=max(dp[i][S],dist(i,j)+dp[i-1][S^(1<1<<j)];
7     }

S表示状态,i和j表示不同的点,并且i和j的枚举不会重复,因为保证了j

对于每一个i,都有1<

例如n=6

状态可以是:

000000

000001

000011

000111

010101

.......

1111111

S^(1<

S&(1<

DAG问题

123

数字三角形

HDU1176

gameboy接馅饼

按照题目给的情况来就行了,从底往上更新

 1 #include
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=1e5+10;
 5 int pile[N][12];
 6 int dp[N][12];
 7 int n,x,t,maxt;
 8 int trimax(int a,int b,int c){
 9     return max(a,max(b,c));
10 }
11 int main(){
12     while(scanf("%d",&n)!=EOF&&n){
13             maxt=0;//*
14             memset(dp,0,sizeof(dp));//*
15             memset(pile,0,sizeof(pile));//*
16         while(n--){
17             scanf("%d %d",&x,&t);
18             pile[t][x]++;
19             maxt=max(maxt,t);
20         }
21     for(int i=0;i<11;i++)//*
22         dp[maxt][i]=pile[maxt][i];
23 
24     for(int i=maxt-1;i>=0;i--){
25         for(int j=0;j<11;j++){
26             if(j==0) dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+pile[i][j];
27             else dp[i][j]=trimax(dp[i+1][j-1],dp[i+1][j],dp[i+1][j+1])+pile[i][j];
28         }
29     }
30     printf("%d\n",dp[0][5]);
31 }
32     return 0;
33 }

 

CF731E funny game

Once upon a time Petya and Gena gathered after another programming competition and decided to play some game. As they consider most modern games to be boring, they always try to invent their own games. They have only stickers and markers, but that won't stop them.

The game they came up with has the following rules. Initially, there are n stickers on the wall arranged in a row. Each sticker has some number written on it. Now they alternate turn, Petya moves first.

One move happens as follows. Lets say there are m ≥ 2 stickers on the wall. The player, who makes the current move, picks some integer k from 2 to m and takes k leftmost stickers (removes them from the wall). After that he makes the new sticker, puts it to the left end of the row, and writes on it the new integer, equal to the sum of all stickers he took on this move.

Game ends when there is only one sticker left on the wall. The score of the player is equal to the sum of integers written on all stickers he took during all his moves. The goal of each player is to maximize the difference between his score and the score of his opponent.

Given the integer n and the initial sequence of stickers on the wall, define the result of the game, i.e. the difference between the Petya's and Gena's score if both players play optimally.

Input
The first line of input contains a single integer n (2 ≤ n ≤ 200 000) — the number of stickers, initially located on the wall.

The second line contains n integers a1, a2, ..., an ( - 10 000 ≤ ai ≤ 10 000) — the numbers on stickers in order from left to right.

Output
Print one integer — the difference between the Petya's score and Gena's score at the end of the game if both players play optimally.

 

题意:有一个序列,两个同学每次选一个前缀和,得到一个新分数,放回原序列最前端,然后希望两位同学的分值相差最大

这里涉及到一个先手后手最优的问题

 1 #include
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=2e5+100;
 5 //a tower~~
 6 int a[N];
 7 int dp[N];
 8 
 9 int main()
10 {
11     ios_base::sync_with_stdio(0);
12     cin.tie(0);
13     cout.tie(0);
14     int n;cin>>n;
15     cin>>a[0];
16     for(int i=1;i){
17         cin>>a[i];
18         a[i]+=a[i-1];
19     }
20     dp[n-1]=a[n-1];//sum    
21     for(int i=n-2;i>=1;i--){
22         dp[i]=max(dp[i+1],a[i]-dp[i+1]);
23     }
24     cout<1];
25     return 0;
26 }

 HDU数塔

 DP一下,马上出发_第3张图片

 1 #include
 2 using namespace std;
 3 #define int long long
 4 #define SYS system("pause");
 5 #define IOS ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 6 const int N=2e5+100;
 7 int a[120][120],dp[120][120],ans,n,h;
 8 int32_t main(){
 9     IOS
10     cin >> n;
11     while(n--){
12         memset(dp, 0, sizeof(dp));
13         memset(a, 0, sizeof(a));
14         cin >> h;
15         for (int i = 1; i <= h;i++)
16             for (int j = 1; j <= i;j++)
17                 cin >> a[i][j];
18         for (int i = 1; i <= h;i++)
19             dp[h][i] = a[h][i];
20         for (int i = h-1; i >= 1; i--){
21             for (int j = 1; j <= i;j++){
22             dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]) + a[i][j];
23             }
24         }
25         cout << dp[1][1] << endl;
26     }
27     //SYS
28     return 0;
29 }

 

最长上升子序列

i.HDU1160

 1 #include
 2 using namespace std;
 3 //最长上升子序列+输出的操作
 4 struct node{
 5     int w, s, ind;
 6     bool operator<(const node &n)const{
 7         if(w>n.w)
 8             return true;
 9         else if(w==n.w)
10             return s < n.s;
11         else
12             return false;
13     }
14 } m[1010];
15 int ind,pre[1010],dp[1010];
16 int main(){
17     while(scanf("%d%d",&m[ind].w,&m[ind].s)!=EOF){
18         m[ind].ind = ind+1;
19         ind++;
20     }
21     int n = ind;
22     sort(m, m + n);
23     //for (int i = 0; i < n;i++)
24     //   dp[i] = 1;
25     memset(pre, -1, sizeof(pre));
26     for (int i = 0; i < n;i++){
27         dp[i] = 1;//不许忘
28         for (int j = 0; j < i; j++){
29             if(m[j].w>m[i].w&&m[j].s<m[i].s){
30                 if(dp[i]1){
31                     dp[i] = dp[j] + 1;
32                     pre[i] = j;//***
33                 }
34             }
35         }
36     }
37     int maxdp = -1,pos=0;
38     for (int i = 0; i < n;i++){
39         if(maxdp<dp[i]){
40             maxdp = dp[i];
41             pos = i;
42         }
43     }
44     cout << dp[pos] << endl;
45     while(pos!=-1){
46         cout << m[pos].ind<<endl;
47         pos = pre[pos];
48     }
49     system("pause");
50     return 0;
51 }

 

背包

背上我的小书包~

背包九讲  very good~

01背包/完全背包/多重背包中的顺序/逆序

对于01背包

内循环是逆序

  dp[j]=max(dp[j],dp[j-w[i]]+v[i]);

保证了每一次取的dp[j-w[i]]是选取前i-1个的最优解(上一层)

 

对于完全背包

内循环是顺序

  dp[j]=max(dp[j],dp[j-w[i]]+v[i]);

这里的dp[j-w[i]]已经被更新为前i个的最优解了(本层)

 

多重背包

内循环是顺序,然后在选取i个的里面,按照数量再展开

神奇代码在这里:

 1 #include 
 2 #include <string.h>
 3 #include 
 4 #include 
 5 #include 
 6 #include 
 7 #include 
 8 #define mem(a,b) memset(a,b,sizeof(a))
 9 using namespace std;
10 int value[1000];//价值
11 int weight[1000];//重量
12 int num[1000];//数量
13 int dp[1000005];
14 int v,m;
15 void bag01(int c,int w)//01背包
16 {
17     int i;
18     for(i=v; i>=c; i--)
19     {
20         if(dp[i]w)
21         {
22             dp[i]=dp[i-c]+w;
23         }
24     }
25 }
26 void bagall(int c,int w)//完全背包
27 {
28     int i;
29     for(i=c; i<=v; i++)
30     {
31         if(dp[i]w)
32         {
33             dp[i]=dp[i-c]+w;
34         }
35     }
36 }
37 void multbag(int c,int w,int n)//多重背包
38 {
39     if(c*n>=v)
40     {
41         bagall(c,w);
42         return ;
43     }
44     int k=1;
45     while(k<=n)
46     {
47         bag01(k*c,k*w);
48         n=n-k;
49         k=k*2;
50     }
51     bag01(n*c,n*w);
52 }
53 int main()
54 {
55     int t;
56     scanf("%d",&t);
57     while(t--){
58         mem(dp,0);
59         scanf("%d%d",&v,&m);
60         for(int i=0; i)
61             scanf("%d%d%d",&value[i],&weight[i],&num[i]);
62         for(int i=0; i)
63             multbag(value[i],weight[i],num[i]);
64         printf("%d\n",dp[v]);
65     }
66     return 0;
67 }

一段废话,以前博客里写的

背上我的小书包系列讲座~

最最最简单的背包长什么样:有一个总容量t

没了.....

题目里给的条件:

有m个物体,放进去

物体m的属性是:w[i],v[i],w表示重量,v表示价值//weight&&value

情景:

明天去春游,有一个承重量最大为t的小书包~~,然后现在老妈买了好多零食

发现带不了那么多,然后就要选择最喜欢的零食加进去;

最后使得“啊哈哈哈明天我春游了我带的都是我喜欢吃的小零食好开心的”这个开心值尽可能大,这是一个求最优解的问题,答案只能有一个。

但是呢,每个小零食的重量是不一样的,可能那个东西很重,但是我喜欢吃,特别特别喜欢,特别特别特别喜欢吃,不让我带这个我就哭给你看。

 

然后呢,我们就要写一个状态转移方程,来看看这个零食是不是已经喜欢到非带不可的地步了

就是这样吧

如果还要分析的话,那么就是,状态就是:这个小零食带不带,此时包包里还能装多少东西,此时得到的最大开心值是多大呢?

然后我们对于这个背包就是定义一个数组:dp[i]

综述完毕,话都在题里了!干杯!

01背包

物品数量有限的情况:

P1060 开心的金明

题意:一个最大承重小于n的,最大物品数为m的背包

      给出m个物体的价格和“重要度“

 要求:不超过背包限制时,能够得到的最大的 “价格*重要度” 的和

 1 #include
 2 using namespace std;
 3 #define int long long
 4 #define sys system("pause")
 5 #define scann(n, m) scanf("%lld%lld", &(n), &(m))
 6 #define prin(n) printf("%lld", (n))
 7 #define fo(a, b) for (int i = (a); i <= (b);i++)
 8 
 9 const int maxn = 30020;
10 int dp[maxn], v[maxn], w[maxn];
11 
12 int32_t main(){
13     int n, m;//总钱数要少于n、、物体个数为m不一定选完
14     scann(n, m);
15     fo(1, m) scann(w[i], v[i]);
16     for (int i = 1; i <= m;i++)//选择1~m个物体,dp表示选取了i个时的最优解
17         for (int j = n; j >= w[i];j--)///价值要保证在范围内
18             dp[j] = max(dp[j],dp[j - w[i]] + w[i] * v[i]);//选取或者不选,由第i-1层转移而来
19     prin(dp[n]);
20     
21     sys;
22     return 0;
23 }

P1048采药

 1 #include
 2 using namespace std;
 3 #define int long long
 4 #define sys system("pause")
 5 #define scann(n, m) scanf("%lld%lld", &(n), &(m))
 6 #define prin(n) printf("%lld", (n))
 7 #define fo(a, b) for (int i = (a); i <= (b);i++)
 8 
 9 const int maxn = 1234;
10 int dp[maxn], w[maxn], v[maxn];
11 int32_t main(){
12     int t, m;
13     scann(t, m);//时间&数目
14     fo(1, m) scann(w[i], v[i]);
15     fo(1,m){
16         for (int j = t; j >= w[i];j--)
17             dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
18     }
19     prin(dp[t]);
20     sys;
21     return 0;
22 }

P1049装箱问题

#include
using namespace std;
#define int long long
#define sys system("pause")
#define scan(n) scanf("%lld", &(n))
#define scann(n, m) scanf("%lld%lld", &(n), &(m))
#define prin(n) printf("%lld", (n))
#define fo(a, b) for (int i = (a); i <= (b);i++)
const int maxn = 23456;
int a[maxn],dp[maxn];
int32_t main(){
    int v,n;
    scann(v, n);
    fo(1,n)
        scan(a[i]);
    //思路就是,装不装这个物体进来
    //那么枚举物体数量,看看加不加入这个背包
    //但是要注意,反向写,找到能加入的最大值,不要直接按照题目进行模拟
    fo(1,n){
        for (int j = v; j >= a[i];j--){
            dp[j] = max(dp[j], dp[j - a[i]] + a[i]);
        }
    }
    prin(v - dp[v]);
    sys;
    return 0;
}

物品数量无限

P1616 疯狂的采药

刚不是有个同学采药嘛

我现在得知他已经疯了

于是——疯狂的采药!!!!

 1 #include
 2 using namespace std;
 3 #define int long long
 4 #define sys system("pause")
 5 #define scann(n, m) scanf("%lld%lld", &(n), &(m))
 6 #define prin(n) printf("%lld", (n))
 7 #define fo(a, b) for (int i = (a); i <= (b);i++)
 8 const int maxn = 1e5 + 100;
 9 int w[maxn], v[maxn], dp[maxn];
10 int32_t main(){
11     int t,m;
12     scann(t, m);
13     fo(1, m) scann(w[i],v[i]);
14     fo(1, m) for (int j = w[i]; j <= t; j++)//我们要更新的不是w[i],w[i]*2,~~~w[i]*n,因为每加入一个新的物品,都会重新更新一下背包在改状态下的数量
15         dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
16     prin(dp[t]);
17     sys;
18     return 0;
19 }

只有第14行有那么一点点区别

让我们把它们截出来(疯狂有时候只在一行之间)

 1 采药:
 2 fo(1,m){
 3     for (int j = t; j >= w[i];j--)
 4         dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
 5 }
 6 
 7 疯狂采药:
 8 fo(1, m) {
 9     for (int j = w[i]; j <= t; j++)
10         dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
11 }

这个前面已经说过原因了

二维和一维~

P1064小A点菜

现在用这道题来尝试着解释一下

dp的一维和二维之间的关系

(有点饿了)

题意:小A有m块钱,现在点菜,一样最多点一道,现在想把钱花光光(一点开源节流的意识都没有)

然后问有多少种方案

如果是一维的话,还是和刚才一样,我们之前的题目用的都是一维的背包

代码如下:

 1 #include
 2 using namespace std;
 3 #define int long long
 4 #define sys system("pause")
 5 #define scan(n) scanf("%lld", &(n))
 6 #define scann(n, m) scanf("%lld%lld", &(n), &(m))
 7 #define prin(n) printf("%lld", (n))
 8 #define fo(a, b) for (int i = (a); i <= (b);i++)
 9 const int maxn = 1020;
10 int dp[maxn], w[maxn];//重量
11 int32_t main(){
12     int n, m;//m是背包容量
13     scann(n,m);
14     dp[0] = 1;//***
15     fo(1, n) scan(w[i]);
16     fo(1, n) 
17         for (int j = m; j >= w[i]; j--)
18             dp[j] += dp[j - w[i]];//加上-->不加这个东西的方法
19     prin(dp[m]);
20     sys;
21     return 0;
22 }

二维+分类讨论:

 1 #include
 2 using namespace std;
 3 #define int long long
 4 #define sys system("pause")
 5 #define scan(n) scanf("%lld", &(n))
 6 #define scann(n, m) scanf("%lld%lld", &(n), &(m))
 7 #define prin(n) printf("%lld", (n))
 8 #define fo(a, b) for (int i = (a); i <= (b);i++)
 9 const int maxn = 1020;
10 int a[maxn], dp[maxn][maxn];
11 int32_t main(){
12     int n, m;
13     scann(n, m);
14     fo(1, n) scan(a[i]);
15     fo(1,n){//前i道菜
16         for (int j = 1; j <= m;j++){//
17             if(j==a[i])
18                 dp[i][j] = dp[i - 1][j] + 1;
19             if(j>a[i])
20                 dp[i][j] = dp[i - 1][j] + dp[i - 1][j - a[i]];
21             if(j<a[i])
22                 dp[i][j] = dp[i - 1][j];
23         }
24     }
25     prin(dp[n][m]);
26     sys;
27     return 0;
28 }

二维和一维的区别就是:

二维的dp[i][j]

这个i表示的是只选取前i个东西时的状态

但是其实,如果用1维的话,我们也是能够保证每次扫一遍的时候可以把方法数更新一遍

怎么说呢

就是我们一维的转移方程是:

dp[j]+=dp[j-w[i]];

这个时候,dp代表的是,重量为j的时候,方案数是多少

TUT我说不来了,下次再说

补充:二维和一维的区别应该是在空间复杂度上,时间上已经没有办法优化了

 

 

然后刚才搞懂了一个非常好玩的东西

从采药和疯狂采药这两个题入手

它们一个叫01背包,一个叫完全背包

01背包只存在这一个选还是不选

我们现在来看它的二维状态转移方程:

dp[i][v]==max{dp[i-1][v],dp[i-1][v-c[i]]+w[i]}

而这个方程是套在哪两个循环里面的呢?

for(int i=1;i<=m;i++){

  for(int j=n;j>=c[i];j--){

    dp[i][j]==max{dp[i-1][j],dp[i-1][j-c[i]]+w[i]}

  }

}

也就是说,第i个选不选,是选取完i-1个之后,这个第i个物体是不放,还是让其他的物体腾出c[i]的空间,(腾出的意思就是:找到容量为v-c[i]的那个状态,把第i个物体放进去呢?

所以对于j循环,我们的循环是n~c[i]

因为我们的dp[i][j]状态   是由dp[i-1][j]//dp[i-1][j-c[i]]推出来的

 

但是对于完全背包就不一样了

它的状态是由它自己的那个推来的

我自己YY出来的公式是
dp[i][v]=max{dp[i][v-c[i]]+w[i],dp[i-1][v]}

 第一个那个是它自己~,第二个是不选取这个东西

所以它的j循环是 c[i]~n

所以,区别就是,顺序&&逆序

 

完全背包(恰好背包)

写一个完全背包的伪代码;

初始化:F[]=-1;

    F[0]=0;

  FOR i (1,n)

    FOR j (m,0)

  if F[]!=-1

    DO:

      DP[j]=MAX(DP[j],DP[j-c[i]]+w[i]); 

 

要注意的就是该容量可不可取

 

贪心与动态规划的结合

这是在浙大校赛中遇到的两道题目,都是01背包,但是融入了贪心的思想

DP一下,马上出发_第4张图片

 

 对于01背包,存在“选”与“不选”两个状态,那么选择的顺序呢?

分析这道题目的条件就会发现,需要一个简单的排序。这样计算的时候,物品体积就是排序后体积的前缀和

并且这道题目,由于有两个k值,所以定义一个二维dp数组,表示选取k1数组的前i个和k2数组的前j个

这道题可以不对不合情况的dp标记-1,不影响结果

 1 #include 
 2 using namespace std;
 3 #define int long long
 4 const int maxn = 2048;
 5 int k1, k2, c, n, m;
 6 int a[maxn],b[maxn],dp[maxn][maxn];
 7 int sum1[maxn], sum2[maxn], ans;
 8 int32_t main(){
 9     int T;
10     cin >> T;
11     while(T--){
12         memset(a, 0, sizeof(a));
13         memset(b, 0, sizeof(b));
14         ans = 0;
15         scanf("%lld%lld%lld", &k1, &k2, &c);
16         scanf("%lld%lld", &n, &m);
17         for (int i = 1; i <= n;i++)
18             scanf("%lld", &a[i]);
19         for (int i = 1; i <= m;i++)
20             scanf("%lld", &b[i]);
21         sort(a+1,a+1+n);
22         sort(b + 1, b + 1 + m);
23         sum1[1] = a[1];
24         sum2[1] = b[1];
25         for (int i = 2; i <= n;i++)
26             sum1[i]= sum1[i - 1] + a[i];
27         for (int i = 2; i <= m;i++)
28             sum2[i] = sum2[i - 1] + b[i];
29         for (int i = 1; i <= n;i++){
30             if(sum1[i]<=c){
31                 dp[i][0] = dp[i - 1][0] + (c - sum1[i]) * k1;
32                 ans = max(dp[i][0], ans);
33             }
34             //else
35             //    dp[i][0] = -1;
36         }
37         for (int i = 1; i <= m;i++){
38             if(sum2[i]<=c){
39                 dp[0][i] = dp[0][i - 1] + (c - sum2[i]) * k2;
40                 ans = max(dp[0][i], ans);
41             }
42             //else
43             //    dp[0][i] = -1;
44         }
45         for (int i = 1; i <= n;i++){
46             for (int j = 1; j <= m;j++){
47                 int t = sum1[i] + sum2[j];
48                 if(t<=c){
49                     dp[i][j] = max(dp[i - 1][j] + (c - t) * k1, dp[i][j - 1] + (c - t) * k2);
50                     ans = max(ans, dp[i][j]);
51                 }
52             }
53         }
54         printf("%lld\n", ans);
55     }
56     //system("pause");
57     return 0;
58 }    

 

 

 ZOJ3958

DP一下,马上出发_第5张图片

 

 这题也是一样

 观察数据,发现,Ci<=100,啊哈哈哈哈

所以Ci的和相同时,只需要看Hi的和的大小,明确了这点,小书包背上~

 1 #include
 2 using namespace std;
 3 #define int long long
 4 int h[600], c[600], dp[50020];
 5 int n, ans, sum;
 6 int32_t main()
 7 {
 8     int T;cin>>T; 9 while(T--){ 10 sum = 0, ans = 0; 11 scanf("%lld", &n); 12 memset(dp, 0, sizeof(dp)); 13 for (int i = 1; i <= n;i++){ 14 scanf("%lld%lld", &h[i], &c[i]); 15 sum += c[i]; 16  } 17 //initially select all of them; 18 for (int i = 1; i <=n;i++){ 19 for (int j = sum; j >= c[i];j--){ 20 dp[j] = max(dp[j], dp[j - c[i]] + h[i]); 21  } 22  } 23 for (int i = 1; i <= sum;i++){ 24 ans = max(dp[i] * dp[i] - i * dp[i] - i * i, ans); 25  } 26 printf("%lld\n", ans); 27  } 28 //system("pause"); 29 return 0; 30 }

 

 

分组背包

P1064金明的预算方案

这道题的标准解法应该是:

   对于每一个物品组进行完全背包“预处理”,这个方法始用于物品组的附件数量任意,我写了一遍,有点不熟练。

这道题直接写也可以,因为附件最多只有3个

  

 1 #include
 2 using namespace std;
 3 int dp[500050];
 4 struct node{
 5     int v, p, q,c;
 6 } a[66], b[66][5];
 7 int cnt[66];
 8 int main(){
 9     int n, m;
10     cin >> n >> m;
11     for (int i = 1; i <= m;i++){
12         scanf("%d%d%d", &a[i].v, &a[i].p, &a[i].q);
13         a[i].c = a[i].v * a[i].p;
14         if(a[i].q){
15             int t = a[i].q;
16             cnt[t]++;
17             b[t][cnt[t]].v = a[i].v;
18             b[t][cnt[t]].c = a[i].c;
19         }
20     }
21     for (int i = 1; i <= m;i++){
22         if(a[i].q==0)
23         for (int j = n; j >= a[i].v;j--){
24             dp[j] = max(dp[j], dp[j - a[i].v] + a[i].c);
25             if(j>=a[i].v+b[i][1].v)
26                 dp[j] = max(dp[j], dp[j - a[i].v - b[i][1].v] + a[i].c + b[i][1].c);
27             if(j>=a[i].v+b[i][2].v)
28                 dp[j] = max(dp[j], dp[j - a[i].v - b[i][2].v] + a[i].c + b[i][2].c);
29             if(j>=a[i].v+b[i][2].v+b[i][1].v)
30                 dp[j] = max(dp[j], dp[j - a[i].v - b[i][1].v - b[i][2].v] + a[i].c + b[i][1].c + b[i][2].c);
31         }    
32     }
33     cout << dp[n];
34     //system("pause");
35 }

 

 

 

  

  

  

区间DP

综述

回忆一下,区间DP是以区间长度从小到大为主导的

可以枚举区间长度,也可以直接利用i,j循环遍历

i.石子合并

来瞻仰一下这个剪贴板: http://xlorpaste.cn/xRdHOE

注意这个石子堆是环状的&&千万要注意实现的方法,别搞错了,但这个已经不属于DP的范畴了~

方法1:

 

 1 #include
 2 using namespace std;
 3 int n, max1, min1 = 9999999;
 4 int dp_max[250][250], dp_min[250][250], sum[250];
 5 int a[250];
 6 int main(){
 7     cin >> n;
 8     for(int i=1;i<=n;i++){
 9         cin >> a[i];
10         a[i+n]=a[i]; 
11     }
12     for (int i = 1; i <= 2 * n;i++)
13         sum[i] = sum[i - 1] + a[i];
14 
15     for (int len = 1; len <= n;len++)
16         for (int i = 1; i <= 2 * n; i++){
17             int j = i + len;
18             if(j<=2*n){
19             dp_min[i][j] = 9999999;
20                 for (int k = i; k ){
21                     dp_min[i][j] = min(dp_min[i][j], dp_min[i][k] + dp_min[k + 1][j] + sum[j] - sum[i - 1]);
22                     dp_max[i][j] = max(dp_max[i][j], dp_max[i][k] + dp_max[k + 1][j] + sum[j] - sum[i - 1]);
23                 }
24             }
25         }
26     for (int i = 1; i <= n;i++){
27         min1 = min(min1, dp_min[i][i + n-1]);
28         max1 = max(max1, dp_max[i][i + n-1]);
29     }
30     printf("%d\n%d", min1, max1);
31     system("pause");
32     return 0;  
33 }

 

方法2:

 1 #include
 2 using namespace std;
 3 int n, max1, min1 = 9999999;
 4 int dp_max[250][250], dp_min[250][250], sum[250];
 5 int a[250];
 6 int main(){
 7     cin >> n;
 8     for(int i=1;i<=n;i++){
 9         cin >> a[i];
10         a[i+n]=a[i]; 
11     }
12     for (int i = 1; i <= 2 * n;i++)
13         sum[i] = sum[i - 1] + a[i];
14 
15     for (int i = 2 * n - 1; i >= 1; i--){
16         for (int j = i + 1; j <= 2 * n;j++){      
17             dp_min[i][j] = 9999999;
18             for (int k = i; k < j; k++){
19                 dp_min[i][j] = min(dp_min[i][j], dp_min[i][k] + dp_min[k + 1][j] + sum[j] - sum[i - 1]);
20                 dp_max[i][j] = max(dp_max[i][j], dp_max[i][k] + dp_max[k + 1][j] + sum[j] - sum[i - 1]);
21             }
22         }
23     }
24     for (int i = 1; i <= n;i++){
25         min1 = min(min1, dp_min[i][i + n-1]);
26         max1 = max(max1, dp_max[i][i + n-1]);
27     }
28     printf("%d\n%d", min1, max1);
29     system("pause");
30     return 0;  
31 }

 

记住大佬说的:DP是以区间长度优先

第一遍的那个循环有点问题

第一遍的代码在此:http://xlorpaste.cn/Vtx337

为什么不直接贴上来?

因为我喜欢这个paste   *OvO*

 

DP一下,马上出发_第6张图片

猜猜哪个是对的,哪个是错的?

完整版,一个正确一个错误

自己画图就懂了

 

http://xlorpaste.cn/xRdHOE

每一次得到的就必须是最优解,不能一遍一遍地更新

 

ii.加分二叉树

两个方法:

WAY1:

 1 #include
 2 #define int long long
 3 #define sys system("pause")
 4 using namespace std;
 5 #define N 32
 6 int dp[N][N], ans,n,root[N][N],v[N];
 7 void build(int n){
 8     for (int i = 1; i <= n;i++){
 9         dp[i][i-1] = 1;//题目指出,空树加分为1
10         dp[i][i + 1] = 1;//题目指出,空树加分为1
11         dp[i][i] = v[i];//叶子节点
12     }
13 }
14 void dfs(int l,int r){//前序输出
15     if(l>r)
16         return;
17     if(l==r){
18         printf("%lld ",l);
19         return;
20     }
21     printf("%lld ", root[l][r]);//
22     dfs(l, root[l][r]-1);//左,由图可知,找左子树只需要root[l][r]-1
23     dfs(root[l][r] + 1, r);//
24 }
25 void init(){
26      scanf("%lld", &n);
27     for (int i = 1; i <= n;i++){
28         scanf("%lld", &v[i]);
29     }
30 }
31 int32_t main(){
32     init();
33     build(n);
34     //dp1:
35     for (int j = 1; j <=n;j++)
36         for (int i = j - 1; i >= 1; i--)
37             for (int k = i; k <= j; k++)
38                 if(dp[i][j]1] * dp[k + 1][j] + dp[k][k]){
39                     dp[i][j] = dp[i][k - 1] * dp[k + 1][j] + dp[k][k];
40                     root[i][j] = k;
41                 }
42     //dp2:
43     for (int i = n; i >= 1;i--)
44         for (int j = i + 1; j <= n;j++)
45             for (int k = i; k <= j;k++)
46                 if(dp[i][j]1] * dp[k + 1][j] + dp[k][k]){
47                     dp[i][j] = dp[i][k - 1] * dp[k + 1][j] + dp[k][k];
48                     root[i][j] = k;
49                 }
50     printf("%lld\n", dp[1][n]);
51     dfs(1, n);
52     sys;
53     return 0;
54 }

WAY2:

 1 #include
 2 #define int long long
 3 #define sys system("pause")
 4 using namespace std;
 5 #define N 32
 6 int dp[N][N], ans,n,root[N][N],v[N];
 7 int sear(int l,int r){
 8     if(dp[l][r])
 9         return dp[l][r];
10     if(l==r)
11         return v[l];
12     if (r < l)
13         return 1;
14     for (int k = l; k <= r;k++){
15         int t = sear(l, k - 1) * sear(k + 1, r) + dp[k][k];
16         if(t>dp[l][r]){
17             dp[l][r] = t;
18             root[l][r] = k;
19         }
20     }
21     return dp[l][r];
22 }
23 void dfs(int l,int r){//前序输出
24     if(l>r)
25         return;
26     if(l==r){
27         printf("%lld ",l);
28         return;
29     }
30     printf("%lld ", root[l][r]);//
31     dfs(l, root[l][r]-1);//左,由图可知,找左子树只需要root[l][r]-1
32     dfs(root[l][r] + 1, r);//
33 }
34 void init(){
35      scanf("%lld", &n);
36     for (int i = 1; i <= n;i++){
37         scanf("%lld", &v[i]);
38         dp[i][i] = v[i];
39     }
40 }
41 int32_t main(){
42     init();
43     printf("%lld\n", sear(1,n));
44     dfs(1, n);
45     sys;
46     return 0;
47 }

 

WAY1要注意的地方:

原则:从小区间--->大区间

从i=n开始和从i=1开始其实都可以操作,那么 为什么我要单独拿出来说呢...

因为我把k=i,k=j那个顺序弄反了

以后要注意!

WAY2:记忆化搜索~树形--->无环

最大子段和

从cube    到square    到O(n)

这是人类思维闪闪发光的时刻!!!!!!!

给一个 数列 negative positive zero都有

求一段连续的,最大子段和

1 int best=0,sum=0;
2 for(int i=0;i)
3 {
4     sum=max(array[i],sum+array[i]);
5     best=max(sun,best);
6 }
7     cout< 
   

多么!clever!多么!wise!

最长上升子序列/最长不上升子序列

LUOGUP1020导弹拦截

DP一下,马上出发_第7张图片

问题1:求最长不上升子序列

问题2:求最长上升子序列-----因为导弹系统只能拦截下降的序列,那么求最长上升子序列,就可以得到导弹系统的数量

    不信你算一算~

O(n*n)解法:

 1 #include
 2 using namespace std;
 3 #define int long long
 4 #define SYS system("pause");
 5 const int N=1e5+100;
 6 int a[N],n,dp[N],ans1,ans2;
 7 int32_t main(){
 8     while(scanf("%lld",&a[++n])!=EOF);
 9     n--;
10     for (int i = n; i >= 1;i--){//以i开头
11         dp[i] = 1;
12         for (int j = i+1; j <= n;j++)
13             if (a[j] <= a[i])//大于等于均可
14                 dp[i] = max(dp[i], dp[j] + 1);
15         ans1 = max(dp[i], ans1);
16     }
17     for (int i = 1; i <= n;i++){//以i结尾
18         dp[i] = 1;
19         for (int j = 1; j < i;j++)
20             if (a[j] < a[i])
21                 dp[i] = max(dp[i], dp[j] + 1);
22         ans2 = max(dp[i], ans2);
23     }
24     cout << ans1<ans2;
25     SYS 
26     return 0;
27 }

HDU1257

导弹拦截:

  每一个导弹拦截系统可以拦截一串不严格下降的序列

  那么,要求最少拦截系统数目,上升子序列的最大长度,就是需要安装的导弹系统的数目

初始化:dp[]=1;

状态:if(a[i]

 1 #include
 2 using namespace std;
 3 #define int long long
 4 #define SYS system("pause");
 5 const int N=1e5+100;
 6 int a[N],n,dp[N];
 7 
 8 int32_t main(){
 9     while(cin >> n){
10         for (int i = 1; i <= n;i++)
11         cin >> a[i];
12         int ans = 0;
13         for (int i = 1; i <= n;i++){
14             dp[i] = 1;
15             for (int j = 1; j <= i;j++)
16                 if (a[j] < a[i])
17                     dp[i] = max(dp[i], dp[j] + 1);
18             ans = max(dp[i], ans);
19         }
20         cout << ans<<endl;
21     }
22     SYS 
23     return 0;
24 }

 

转载于:https://www.cnblogs.com/guaguastandup/p/10628104.html

你可能感兴趣的:(DP一下,马上出发)