不想多说什么,再次错过一次 AK 机会

T1 青蛙的烦恼

蛤の烦恼
⃢ — ⃢
设 $f(i,j,k)$为在区间 $[l,r]$内,位置为 k 时遍历所有坐标的最短路径。
$dis(i,j)$表示从点 i 到点 j 的距离
这样空间复杂度为 $n^3$,1e9 无法接受
不难发现 k 只可能为 i 或者为 j
所以设 $f(i,j,t)$为当前位置为 i,区间长度为 j。t=0 表示区间在 i 右边,t=1 表示区间在左边。
空间复杂度降到了 $n^2$
于是就轻松解决了

代码:

#include <bits/stdc++.h>
#define dis(a,b) (sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b])))
using namespace std;
int n;
double x[1000],y[1000],f[1000][1000][2];
bool book[1000][1000][2];
double dfs(int cur,int l,int t)
{
    if(book[cur][l][t])return f[cur][l][t];
    book[cur][l][t]=1;
    if(l==1)return 0;
    if(!t)
        f[cur][l][t]=
            min(dfs(cur+l-1,l-1,1)+dis(cur,cur+l-1),dfs(cur+1,l-1,0)+dis(cur,cur+1));
    else
        f[cur][l][t]=
            min(dfs(cur-l+1,l-1,0)+dis(cur,cur-l+1),dfs(cur-1,l-1,1)+dis(cur,cur-1));
    return f[cur][l][t];
}
int main()
{
    freopen("frog.in","r",stdin),freopen("frog.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lf%lf",&x[i],&y[i]);
    printf("%.3f\n",dfs(1,n,0));
    return 0;
}

T2 警卫安排

我真是太弱了
没事多叉转二叉干啥
转二叉理论上也能做,只是需要讨论 4 种情况。
所以不如不转



//以上截图自 ppt
下面代码中,0 表示安排警卫,1 表示被父亲看到,2 表示被儿子看到。
代码:

#include <bits/stdc++.h>
using namespace std;
int n,v[1000],root,f[1000][3];
bool book[1000];
vector<int> g[1000];
int dfs(int u,int w)
{
    if(f[u][w]!=-1)return f[u][w];
    switch(f[u][w]=0,w)
    {
        case 0:
            for(int i=0;i<g[u].size();i++)
                f[u][w]+=min(dfs(g[u][i],0),min(dfs(g[u][i],1),dfs(g[u][i],2)));
            f[u][w]+=v[u];break;
        case 1:
            for(int i=0;i<g[u].size();i++)f[u][w]+=min(dfs(g[u][i],0),dfs(g[u][i],2));
            break;
        case 2:
            f[u][w]=1e8;
            for(int i=0;i<g[u].size();i++)
                f[u][w]=min(f[u][w],dfs(g[u][i],0)-min(dfs(g[u][i],0),dfs(g[u][i],2)));
            for(int i=0;i<g[u].size();i++)
                f[u][w]+=min(dfs(g[u][i],0),dfs(g[u][i],2));
        break;
    }
    return f[u][w];
}
int main()
{
    freopen("security.in","r",stdin),freopen("security.out","w",stdout);
    scanf("%d",&n);
    for(int i=1,j,k;i<=n;i++)
    {
        scanf("%d",&j),scanf("%d%d",&v[j],&k);
        for(int i1=1,j1;i1<=k;i1++)scanf("%d",&j1),g[j].push_back(j1),book[j1]=1;
    }
    for(int i=1;i<=n;i++)if(!book[i]){root=i;break;}
    memset(f,-1,sizeof(f)),printf("%d\n",min(dfs(root,0),dfs(root,2)));
    return 0;
}

T3 最长上升子序列

最水的一题。
在 1…k 跑一遍 lis,再再 k+1…n 跑一遍 lis 即可。
第二次跑 lis 的时候需要筛掉所有小于第 k 个数的元素
答案为 $f[k]+max\{f[k+1…n]\}$
也可以一开始先筛掉所有小于第 k 个数的元素,再跑一遍 lis
需要用 nlogn 的算法

代码:

#include <bits/stdc++.h>
using namespace std;
int n,k,a[200005],e[200005],cnt,f[200005],ans;
int main()
{
    freopen("lis.in","r",stdin),freopen("lis.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=k;i++)
    {
        f[i]=(int)(lower_bound(e+1,e+1+cnt,a[i])-e);
        if(f[i]>cnt)e[++cnt]=a[i];else if(a[i]<e[f[i]])e[f[i]]=a[i];
    }
    memset(e,0,sizeof(e)),cnt=0;
    for(int i=k+1;i<=n;i++)
        if(a[i]>a[k])
        {
            f[i]=(int)(lower_bound(e+1,e+1+cnt,a[i])-e);
            if(f[i]>cnt)e[++cnt]=a[i];else if(a[i]<e[f[i]])e[f[i]]=a[i];
            ans=max(ans,f[i]);
        }
    printf("%d\n",ans+f[k]);
    return 0;
}

T4 最短回文串

区间 dp
设 $f(i,j)$为把 $[i,j]$这段子串变为一个回文串的最小代价
当 $s[i]==s[j]$时 $f(i,j)=f(i+1,j-1)$
否则 $f(i,j)=min\{f(i+1,j),f(i,j-1)\}+1$

代码:

#include <bits/stdc++.h>
using namespace std;
int n,f[5005][5005];
char s[5005];
int main()
{
    freopen("palindrome.in","r",stdin),freopen("palindrome.out","w",stdout);
    scanf("%d%s",&n,s);
    for(int l=1;l<n;l++)
        for(int i=0;i+l<n;i++)
        {
            f[i][i+l]=min(f[i+1][i+l],f[i][i+l-1])+1;
            if(s[i]==s[i+l])f[i][i+l]=min(f[i][i+l],f[i+1][i+l-1]);
        }
    printf("%d\n",f[0][n-1]);
    return 0;
}
分类: 文章

XZYQvQ

炒鸡辣鸡的制杖蒟蒻一枚QvQ

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注