LIGHTOJ1422
这个题可以从两种角度来考虑,第一种是根据题意中的套衣服设定,如果出现一件衣服是之前穿过的,那么枚举讨论新的同款衣服需不需要额外穿一次。第二种是根据刷区间设定(HDU2476),如果现在出现了一个之前出现过的的颜色,那么讨论这个颜色是否可以从之前直接刷过来。(实际上两种设定一样)
那么根据设定可以确定这样的状态转移:对于任意一件衣服,如果在此之前的区间(这个区间为i->j)出现了一件和当前相同的衣服,那么讨论是否需要利用之前的衣服,还是新穿上这样的衣服。
即dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j-1])(默认新区间是从右侧扩张的,j-1是扩张之前的区间右端点)。
当然,对于每个长度为1的区间,也就是单个元素时,根据题目设定,容易知道dp[i][i]=1
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
//#define endl '\n'
#define DETERMINATION main
#define For(a,b,c,d) for(int a=b;a<=c;a+=d)
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug std::cout<<"procedures above are available"<<"\n";
#define BigInteger __int128
using namespace std;
const long long INF = 2147483647;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
// BigInteger tmp = 0, si = 1;char c; c = getchar();
// while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
// while (isdigit(c))
// {tmp = tmp * 10 + c - '0';c = getchar();}
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {x = -x;putchar('-');}
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
// }
/**Operation Overlord 1944.6.6 Daybreak**/
/**Last Remote**/
ll arr[500];
ll dp[210][210];
int DETERMINATION()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0),std::cout.tie(0);
ll t;
cin>>t;
for(int y=1;y<=t;y++)
{
ll n;
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j]=0;
for(int i=1;i<=n;i++)
cin>>arr[i];
for(int i=1;i<=n;i++)
dp[i][i]=1;
//cout<<dp[1][1]<<endl;
for(int len=2;len<=n;len++)
{
for(int i=1;i+len-1<=n;i++)
{
int j=i+len-1;
dp[i][j]=dp[i][j-1]+1;
for(int k=i;k<=j-1;k++)
{
if(arr[k]==arr[j])
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j-1]);
}
}
}
cout<<"Case "<<y<<": "<<dp[1][n]<<endl;
}
return 0;
}
POJ2955
本题是一个关于括号序列的题目,这里要求的是最长合法括号子序列,所以并不能用栈来解决问题。因为是一个关于区间最优解的问题,所以选择使用区间DP。
对于本题而言,它的区间是从两端发散,由小扩展到大的,而扩展的条件是区间两端存在一对合法括号。扩展之后再划分区间即可,此时这个划分区间的用意是由两个子区间拼接形成新的区间。
//#include<pch.h>
#include <iostream>
#include <cstdio>
//#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
//#define endl '\n'
#define DETERMINATION main
#define For(a,b,c,d) for(int a=b;a<=c;a+=d)
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug std::cout<<"procedures above are available"<<"\n";
#define BigInteger __int128
using namespace std;
const long long INF = 2147483647;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
// BigInteger tmp = 0, si = 1;char c; c = getchar();
// while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
// while (isdigit(c))
// {tmp = tmp * 10 + c - '0';c = getchar();}
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {x = -x;putchar('-');}
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
// }
/**Operation Overlord 1944.6.6 Daybreak**/
/**Last Remote**/
char arr[5000];
ll dp[120][120];
int DETERMINATION()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
while (cin >> (arr + 1))
{
if (arr[1] == 'e')
break;
reset(dp, 0);
ll n = strlen(arr + 1);
for (int len = 2; len <= n; len++)
{
for (int i =1; i + len - 1 <= n; i++)
{
ll j = i + len - 1;
if ((arr[i] == '['&&arr[j] == ']') || (arr[i] == '('&&arr[j] == ')'))
dp[i][j] = dp[i + 1][j - 1] + 2;
for (int k = i; k <= j - 1; k++)
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j]);
}
}
cout << dp[1][n] << endl;
}
//cout << power[0] << endl;
return 0;
}
CF149D
本题分三种情况:
第一种是 (),这是最底层的情况,只需要将合法的dp[l][r][i][j]赋值为1即可。
第二种是 (......),这可以看做是由多组第一种情况在两边加上一对括号扩展而来的,在扩展中注意判断相邻的括号不能相同颜色即可。也就是dp[l][r][m][n]+=dp[l+1][r-1][i][j]。
第三种是(......)(......)这个时候就需要利用区间DP分割区间了,不过枚举非常耗时间,鉴于题目中给定的是一个正确的括号序列(两两配对),所以可以直接用一个栈把每个左括号对应的右括号记录下来,然后在这里就很容易找出区间分隔点。
即dp[l][r][i][j]=dp[i][correspond(i)][i][j]*dp[correspond(i)+1][r][i][j]。由于括号必定是两两配对的,所以correspond(i+1)必然是与correspond(i)相邻且方向相异的,因为如果方向相同的话在区间左侧就再没有括号与之配对了。
本题难以从底部问题来推导最终问题,所以采用记忆化搜索自上而下,再回溯计算答案。
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
//#define endl '\n'
#define DETERMINATION main
#define For(a,b,c,d) for(int a=b;a<=c;a+=d)
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug std::cout<<"procedures above are available"<<"\n";
#define BigInteger __int128
using namespace std;
const long long INF = 2147483647;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
// BigInteger tmp = 0, si = 1;char c; c = getchar();
// while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
// while (isdigit(c))
// {tmp = tmp * 10 + c - '0';c = getchar();}
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {x = -x;putchar('-');}
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
// }
/**Operation Overlord 1944.6.6 Daybreak**/
/**Last Remote**/
char arr[780];
ll dp[800][800][3][3];
ll correspond[870];
stack<ll>stk;
void dfs(ll CL, ll CU)
{
if (CL + 1 == CU)
dp[CL][CU][0][1] = dp[CL][CU][0][2] = dp[CL][CU][1][0] = dp[CL][CU][2][0] = 1;
else if (correspond[CL] == CU)
{
dfs(CL + 1, CU - 1);
for (int i = 0; i <= 2; i++)
{
for (int j = 0; j <= 2; j++)
{
if (j != 1)
dp[CL][CU][0][1] = (dp[CL][CU][0][1] + dp[CL + 1][CU - 1][i][j]) % mod;
if (j != 2)
dp[CL][CU][0][2] = (dp[CL][CU][0][2] + dp[CL + 1][CU - 1][i][j]) % mod;
if (i != 1)
dp[CL][CU][1][0] = (dp[CL][CU][1][0] + dp[CL + 1][CU - 1][i][j]) % mod;
if (i != 2)
dp[CL][CU][2][0] = (dp[CL][CU][2][0] + dp[CL + 1][CU - 1][i][j]) % mod;
}
}
}
else
{
dfs(CL, correspond[CL]);
dfs(correspond[CL] + 1, CU);
for (int i = 0; i <= 2; i++)
{
for (int j = 0; j <= 2; j++)
{
for (int k = 0; k <= 2; k++)
{
for (int m = 0; m <= 2; m++)
{
if ((j == 1 && k == 1) || (j == 2 && k == 2))
continue;
dp[CL][CU][i][m] = (dp[CL][CU][i][m] + dp[CL][correspond[CL]][i][j]%mod* dp[correspond[CL] + 1][CU][k][m]%mod)%mod;
}
}
}
}
}
}
int DETERMINATION()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
cin >> arr + 1;
ll len = strlen(arr + 1);
for (int i = 1; i<=len; i++)
{
if (arr[i] == '(')
stk.push(i);
else
{
correspond[stk.top()] = i;
correspond[i] = stk.top();
stk.pop();
}
}
dfs(1, len);
ll ans = 0;
for (int i = 0; i <= 2; i++)
{
for (int j = 0; j <= 2; j++)
{
ans = (ans + dp[1][len][i][j]) % mod;
//cout << dp[1][len][i][j] << endl;
}
}
cout << ans << endl;
return 0;
}
POJ1651
本题的主要难点在于DP数组上。如果仍然把dp[l][r]认为是[l,r]一整个连续区间内的值中,取一个数的最大值,那就很难做出这道题。所以可以换个思路,认为dp[l][r]为区间内两边只剩下l,r和某个数的最大乘积。
那么这个题的状态转移方程就可以认定为:dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[j]*a[k]),其中dp[i][k],dp[k+1][j]就是两个在之前的循环中已经解决的子问题,可以直接调用结果。(注意两个子区间都必须包括K)
//#include<pch.h>
#include <iostream>
#include <cstdio>
//#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
//#define endl '\n'
#define DETERMINATION main
#define For(a,b,c,d) for(int a=b;a<=c;a+=d)
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug std::cout<<"procedures above are available"<<"\n";
#define BigInteger __int128
using namespace std;
const long long INF = 2147483647;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
// BigInteger tmp = 0, si = 1;char c; c = getchar();
// while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
// while (isdigit(c))
// {tmp = tmp * 10 + c - '0';c = getchar();}
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {x = -x;putchar('-');}
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
// }
/**Operation Overlord 1944.6.6 Daybreak**/
/**Last Remote**/
ll dp[120][120];
ll arr[1200];
int DETERMINATION()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
ll n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> arr[i];
for (int i = 1; i < n; i++)
dp[i][i + 1] = 0;
for (int len = 2; len <=n; len++)
{
for (int i = 1; len + i<=n; i++)
{
ll j = len + i;
for (int k = i + 1; k <= j -1; k++)
{
if (dp[i][j] != 0)
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j] + arr[k] * arr[i] * arr[j]);
else
dp[i][j] = dp[i][k] + dp[k][j] + arr[k] * arr[i] * arr[j];
}
}
}
cout << dp[1][n] << endl;
return 0;
}
HDU4283
本题需要用特殊的区间dp来解决。
所谓特殊主要是特殊在区间合并上,这里需要假定第i个人第K个上场,这就演化出了两个子问题dp[i+1][k],dp[k+1][j],在这两个的子问题的基础上还需要加上当前人的愤怒值和在他之后上场的人的愤怒值。
从初始化的角度来说,当每个子区间只有一个人的时候,愤怒值自然为0,其余的设定为inf,但是在这里k+1可能会越界,所以还需要加一个必要的边界保护。
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
//#define endl '\n'
#define DETERMINATION main
#define For(a,b,c,d) for(int a=b;a<=c;a+=d)
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug std::cout<<"procedures above are available"<<"\n";
#define BigInteger __int128
using namespace std;
const long long INF = 2147483647;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
// BigInteger tmp = 0, si = 1;char c; c = getchar();
// while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
// while (isdigit(c))
// {tmp = tmp * 10 + c - '0';c = getchar();}
// return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
// if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
// return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
// if (x < 0)
// {x = -x;putchar('-');}
// if (x > 9) output(x / 10);
// putchar(x % 10 + '0');
// }
/**Operation Overlord 1944.6.6 Daybreak**/
/**Last Remote**/
ll dp[200][200];
ll arr[230];
ll sum[230];
int DETERMINATION()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0), std::cout.tie(0);
ll t;
cin >> t;
for (int y = 1; y <= t; y++)
{
ll n;
cin >> n;
reset(sum, 0);
for (int i = 1; i <= n; i++)
{
cin >> arr[i];
sum[i] = sum[i - 1] + arr[i];
}
reset(dp, 0x3f);
for (int i = 1; i <= n; i++)
dp[i][i] = 0;
for (int l = 1; l < n; l++)
{
for (int i = 1; i + l <= n; i++)
{
ll j = i + l;
for (int k = i; k <=j; k++)
dp[i][j] = min(dp[i][j], (k - i)*arr[i] + (k - i+1)*(sum[j] - sum[k]) + dp[min(i + 1,k)][k] + dp[min(k + 1,(int)j)][j]);
}
}
cout << "Case #" << y << ": " << dp[1][n] << endl;
}
return 0;
}