一般是由长度小的子问题推到长度大的子问题,解法一般比较固定,先枚举长度再枚举左端点 最后枚举中间的分割点
有时候第一第二层分别枚举左端点和右端点 。看后效性 (靠感觉) 吧2333
时间复杂度: N 3 N^3 N3 空间复杂度: N 2 N^2 N2
https://loj.ac/problem/10147
之前写过的:https://blog.csdn.net/qq_36038511/article/details/71023592
#include
#include
#include
using namespace std;
int mn[210][210],mx[210][210];//[长度][左端点]
int a[410],sum[410];
int main()
{
memset(mn,63,sizeof(mn));
memset(mx,0,sizeof(mx));
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++) {scanf("%d",&a[i]);a[i+n]=a[i];}
for (int i=1;i<=2*n-1;i++)
{
mn[1][i]=mx[1][i]=0; mn[0][i]=mn[0][i]=0;
sum[i]=sum[i-1]+a[i];
}
for (int k=2;k<=n;k++)
{
for (int i=1;i<=2*n-1-k;i++)
{
int j=i+k-1;
for (int u=i;u
https://loj.ac/problem/10148
思路&之前的题解同上
#include
#include
#include
using namespace std;
long long f[210][210],st[210],ed[210];
int main()
{
int n;
scanf("%d",&n);
scanf("%lld%lld",&st[1],&ed[1]); st[n+1]=st[1]; ed[n+1]=ed[1];
for (int i=2;i<=n-1;i++)
{
scanf("%lld",&ed[i]);
st[i]=ed[i-1];
st[i+n]=st[i]; ed[i+n]=ed[i];
}
st[n]=st[2*n]=ed[n-1];ed[n]=ed[n*2]=st[1];
for (int k=2;k<=n;k++)//长度
{
for (int i=1;i<=2*n-k;i++)//左端点
{
int j=i+k-1;
for (int u=i;u
https://loj.ac/problem/10149
通过画图可以知道 五边形可以由四边形/三边形推出,六边形可以由五边形/四边形/三边形推出……
给每个顶点标号1~n,f[i][j]表示跨越了i个顶点 起点为j 一样的套路
非常恶心的高精度
#include
#include
#include
struct node
{
int a[510],len;
node()
{
memset(a,63,sizeof(a));
len=99;
}
}f[110][110],a[110];
//f[i][j]:顶点i到顶点j 的最小乘积之和
using namespace std;
node chengfa(node x,node y)
{
node c;
c.len=x.len+y.len;
memset(c.a,0,sizeof(c.a));
for (int i=1;i<=x.len;i++)
{
for (int j=1;j<=y.len;j++)
{
c.a[i+j-1]+=x.a[i]*y.a[j];
}
}
for (int i=1;i<=c.len;i++)
{
c.a[i+1]+=c.a[i]/10;
c.a[i]%=10;
}
while (c.a[c.len]==0&&c.len>1) c.len--;
return c;
}
node jiafa(node x,node y)
{
node c;
c.len=max(x.len,y.len)+1;
memset(c.a,0,sizeof(c.a));
for (int i=1;i<=c.len;i++)
{
c.a[i]=x.a[i]+y.a[i];
}
for (int i=1;i<=c.len;i++)
{
c.a[i+1]+=c.a[i]/10;
c.a[i]%=10;
}
while (c.a[c.len]==0&&c.len>1) c.len--;
return c;
}
void upd(node x,node &y)
{
if (x.len=1;i--)
{
if (x.a[i]y.a[i]) {break;}
}
}
}
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
int x;
memset(a[i].a,0,sizeof(a[i].a)); a[i].len=0;
scanf("%d",&x);
while (x!=0)
{
a[i].a[++a[i].len]=x%10;
x/=10;
}
a[i+n]=a[i];
}
for (int i=1;i<=n;i++)
{
memset(f[i][i].a,0,sizeof(f[i][i])); f[i][i].len=0;
memset(f[i][i+1].a,0,sizeof(f[i][i+1]));f[i][i+1].len=0;
memset(f[i-1][i].a,0,sizeof(f[i-1][i]));f[i-1][i].len=0;
}
for (int l=2;l=1;i--)
{
printf("%d",f[1][n].a[i]);
}
printf("\n");
return 0;
}
https://loj.ac/problem/10150
第一眼看成之前做进阶的时候的括号画家2333
一开始写了预处理括号然后区间dp 但是还是too young too simple了
([%@*!]) 可以把中间的处理掉之后直接加上两层括号啊
注意 ([)]
这种样例,注意两种括号的配对 “(” 不能对"]" (错的都是什么沙雕错误)
#include
#include
#include
using namespace std;
int f[110][110],a[110];
char ch[110];
int main()
{
memset(f,63,sizeof(f));
scanf("%s",ch+1);
int n=strlen(ch+1);
for (int i=1;i<=n;i++)
{
if (ch[i]=='(') a[i]=-1;
else if (ch[i]==')') a[i]=1;
else if (ch[i]=='[') a[i]=-2;
else if (ch[i]==']') a[i]=2;
f[1][i]=1; f[0][i]=0;
}
for (int k=2;k<=n;k++)
{
for (int i=1;i<=n-k+1;i++)
{
int j=i+k-1;
for (int u=i;u<=j;u++)
{
f[k][i]=min(f[k][i],f[u-i+1][i]+f[j-u][u+1]);
}
if (a[i]+a[j]==0&&a[i]<0) f[k][i]=min(f[k][i],f[k-2][i+1]);
}
}
printf("%d\n",f[n][1]);
return 0;
}
https://loj.ac/problem/10151
这题的题意本身是难点…读懂题花了一下午
感性的理解一下:倒过来做,从最后合体的那个的开始往两边扩张,往回推分离的过程
要分离到不能分离才合体!
至于方案…写个pre然后倒搜
不要学我写高精度!!
#include
#include
#include
using namespace std;
struct node
{
long long s;
int p;
node()
{
s=0; p=0;
}
}f[310][610];
int a[310],sum[310];
struct nodel
{
int k,i,p;
}list[610];
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
f[1][i].s=0;f[1][i].p=i;
//sum[i]=a[i]+sum[i-1];
}
for (int k=2;k<=n;k++)
{
for (int i=1;i<=n-k+1;i++)
{
int j=i+k-1;
for (int u=i;uf[k][i].s)
{
f[k][i].s=f[u-i+1][i].s+f[j-u][u+1].s+(a[i]+a[j])*a[u];
f[k][i].p=u;
}
}
}
}
printf("%lld\n",f[n][1]);
int head=1,tail=1,k=n-1;
list[1].p=f[n][1].p; list[1].k=n; list[1].i=1;
while (head<=tail)
{
if (list[head].k<=2||list[head].i==0) {head++; continue;}
int p=f[list[head].k][list[head].i].p;
list[++tail].k=p-list[head].i+1;
list[tail].i=list[head].i;
list[tail].p=f[list[tail].k][list[tail].i].p;
list[++tail].k=list[head].i+list[head].k-1-p;
list[tail].i=p+1;
list[tail].p=f[list[tail].k][list[tail].i].p;
head++;
}
for (int i=1;i<=tail;i++)
if (list[i].k>=2) printf("%d ",list[i].p);
printf("\n");
return 0;
}
https://loj.ac/problem/10152
一开始还想贪心直接取两端的最小值但是第二个样例就卡掉了……
跟上一题一样的骚操作:对于每一行,相当于从最后结束的那个数向两边扩展 因为每次只用选一个数 不用枚举分割点
#include
#include
#include
using namespace std;
struct node
{
int len,a[100];
node()
{
memset(a,0,sizeof(a));
len=0;
}
}f[90][90][90],pi[90];
int a[90][90];
node chengfa(node x,int y)
{
node c; c.len=x.len;
for (int i=1;i<=x.len;i++)
{
c.a[i]=x.a[i]*y;
}
for (int i=1;i<=c.len;i++)
{
if (c.a[i]>9)
{
c.a[i+1]+=c.a[i]/10;
c.a[i]%=10;
if (i==c.len) c.len++;
}
}
while (c.len>1&&c.a[c.len]==0) c.len--;
return c;
}
node jiafa(node x,node y)
{
node c;c.len=max(x.len,y.len)+1;
for (int i=1;i<=c.len;i++)
c.a[i]=x.a[i]+y.a[i];
for (int i=1;i<=c.len;i++)
{
c.a[i+1]+=c.a[i]/10;
c.a[i]%=10;
}
while (c.len>1&&c.a[c.len]==0) c.len--;
return c;
}
node cpr(node x,node y)
{
if (x.len>y.len) return x;
else if (x.leny.a[i]) return x;
else if (x.a[i]
完结撒花!