P1880 [NOI1995] 石子合并
思路
对于区间dp,引用OI Wiki里的介绍:
对于环形的情况,我们讲这条链延申两倍,变成2*n堆,其中第i堆和第i+n堆相同。用动态规划求解后,求出
d
p
[
1
]
[
n
]
,
d
p
[
2
]
[
n
+
1
]
.
.
.
d
p
[
n
−
1
]
[
2
n
−
2
]
dp[1][n],dp[2][n+1]...dp[n-1][2n-2]
dp[1][n],dp[2][n+1]...dp[n−1][2n−2]的最大/最小值。
需要注意的是dp2[i][j]需要赋初值为一个大整数,但不能将开始循环的区间长度设置为1,要设置为2,否则dp2[i][j]就全部为这个大整数的值了 = =
P1063 [NOIP2006 提高组] 能量项链 和这道题类似
实现
#include <bits/stdc++.h>
#define maxn 220
using namespace std;
typedef long long ll;
int n;
int s[maxn];
int a[maxn];
int dp1[maxn][maxn];
int dp2[maxn][maxn];
int main()
{
int t;
scanf("%d\n",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i+n] = a[i];
}
for(int i=1;i<=2*n;i++)
{
s[i] = s[i-1]+a[i];
}
for(int len=2;len<=n;len++) //len从2开始
{
for(int l=1;l<=(2*n-len);l++)
{
int r = l+len-1;
dp2[l][r] = 10e8;
for(int k = l;k<r && k<=2*n-1;k++)
{
dp1[l][r] = max(dp1[l][r],dp1[l][k]+dp1[k+1][r]+s[r]-s[l-1]);
dp2[l][r] = min(dp2[l][r],dp2[l][k]+dp2[k+1][r]+s[r]-s[l-1]);
}
}
}
int max1 = 0;
int min1 = 10e8;
for(int i=1;i<n;i++)
max1 = max(max1,dp1[i][i+n-1]);
for(int i=1;i<n;i++)
min1 = min(min1,dp2[i][i+n-1]);
cout<<min1<<endl;
cout<<max1;
return 0;
}
P3205 [HNOI2010]合唱队
思路
d p 1 [ i ] [ j ] dp1[i][j] dp1[i][j]记录区间 [ i , j ] [i,j] [i,j]且最后一个插入的数为 a [ i ] a[i] a[i]的排列方法总数, d p 2 [ i ] [ j ] dp2[i][j] dp2[i][j]记录区间 [ i , j ] [i,j] [i,j]且最后一个插入的数为 a [ j ] a[j] a[j]的排列方法总数。
实现
#include <bits/stdc++.h>
#define maxn 1010
using namespace std;
typedef long long ll;
int n;
int mod = 19650827;
int s[maxn];
int a[maxn];
int dp1[maxn][maxn];
int dp2[maxn][maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp1[i][i] = 1;
//dp2[i][i] = 1;
}
for(int len=2;len<=n;len++)
{
for(int l = 1;l<=(n-len)+1;l++)
{
int r = l+len-1;
dp1[l][r] = ( (a[l]<a[l+1]) * dp1[l+1][r] + (a[l]<a[r]) * dp2[l+1][r] )%mod;
dp2[l][r] = ( (a[r]>a[l]) * dp1[l][r-1]+(a[r]>a[r-1]) * dp2[l][r-1] )%mod;
}
}
cout<<(dp1[1][n]+dp2[1][n])%mod;
return 0;
}
P3146 [USACO16OPEN]248 G
思路
和石子合并类似
实现
#include <bits/stdc++.h>
#define maxn 300
using namespace std;
typedef long long ll;
int n;
int s[maxn];
int a[maxn];
int dp[maxn][maxn];
int ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[i][i] = a[i];
}
for(int len=2;len<=n;len++)
{
for(int l=1;l<=n-len+1;l++)
{
int r = l+len-1;
for(int k=l;k<r;k++)
{
if(dp[l][k]==dp[k+1][r] && dp[l][k]!=0)
{
dp[l][r] = max(dp[l][r],dp[l][k]+1);
ans = max(ans,dp[l][r]);
}
}
}
}
cout<<ans<<endl;
return 0;
}
P4170 [CQOI2007]涂色
思路
对于一个已经涂好的区间
[
i
,
j
]
[i,j]
[i,j],如果
i
i
i和
j
j
j颜色相同,则
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
]
[
j
−
1
]
)
dp[i][j] = min(dp[i-1][j],dp[i][j-1])
dp[i][j]=min(dp[i−1][j],dp[i][j−1])(只要上次再多涂一块就行)
i
i
i和
j
j
j颜色不同时需要枚举分割点
实现
#include <bits/stdc++.h>
#define maxn 300
using namespace std;
typedef long long ll;
int n;
string t;
char a[maxn];
int dp[maxn][maxn];
int main()
{
cin>>t;
n = t.size();
for(int i=1;i<=n;i++)
{
a[i] = t[i-1];
dp[i][i] = 1;
}
for(int len=2;len<=n;len++)
{
for(int l=1;l<=n-len+1;l++)
{
int r = l+len-1;
dp[l][r] = 10e8;
if(a[r] == a[l])
{
dp[l][r] = min(dp[l][r-1],dp[l+1][r]);
continue;
}
for(int k=l;k<r;k++)
{
dp[l][r] = min(dp[l][r],dp[l][k]+dp[k+1][r]);
}
}
}
cout<<dp[1][n]<<endl;
return 0;
}
CF607B Zuma
思路
思路来自:洛谷题解区题解
读入n的时候scanf(“%d\n”,&n);可以AC
scanf(“%d”,&n);的话第45个点会wa,不理解hhh
实现
#include <bits/stdc++.h>
#define maxn 505
using namespace std;
typedef long long ll;
int n;
int a[maxn];
int dp[maxn][maxn];
int ans;
int main()
{
memset(dp,127,sizeof(dp));
scanf("%d\n",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[i][i] = 1;
}
for(int i=1;i<n;i++)
{
if(a[i]==a[i+1]) dp[i][i+1] = 1;
else dp[i][i+1] = 2;
}
for(int len=3;len<=n;len++)
{
for(int l=1;l<=n-len+1;l++)
{
int r = l+len-1;
if(a[r] == a[l])
{
dp[l][r] = dp[l+1][r-1];
}
for(int k=l;k<r;k++)
{
dp[l][r] = min(dp[l][r],dp[l][k]+dp[k+1][r]);
}
}
}
cout<<dp[1][n]<<endl;
return 0;
}
P4933 大师
实际上是求:在n个数字中选一些数使其构成等差数列(一个 or 两个数字也算等差数列)问一共有多少种选法?
但是自己没做出来 = =
欢迎指正