动态规划自虐行为刷题——

P1220 关路灯 P2279 [HNOI2003]消防局的设立 P1373 小a和uim之大逃离 P1005 矩阵取数游戏

先说关路灯吧,几个世纪不打区间DP都忘了;

一个老人来回关灯,求最小消耗功率

主要思路是从起点(家)拓展区间,由小区间转移到大区间,因为有继续向前走还有回头关灯两种选择

我们设f[i][j][k]表示区间i到j已经关完,k=0表示现在老人停在i节点上,k=1表示停在j节点上。

前缀和求两侧没有关的灯的功率;

f[i][j][0]=min(f[i+1][j][0]+(a[i+1]-a[i])*(sum[i]+sum[n]-sum[j]),f[i+1][j][1]+(a[j]-a[i])*(sum[i]+sum[n]-sum[j]));

ij关掉,由i+1,j在左在右转移;

既然都关完停在了i节点,一定是i没有关,而i+1到j都已经关上了;

实际上转移过程像一滴水摊开一样

f[i][j][1]同理

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=60;
int n,c;
int a[maxn],b[maxn],sum[maxn];
int f[maxn][maxn][2];

int main()
{
    memset(f,127,sizeof(f));
    scanf("%d%d",&n,&c);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i],&b[i]);
        sum[i]=sum[i-1]+b[i];
    }
    f[c][c][0]=0;f[c][c][1]=0;
    for(int l=2;l<=n;l++)
    {
        for(int i=1;i+l-1<=n;i++)
        {
            int j=i+l-1;
            f[i][j][0]=min(f[i+1][j][0]+(a[i+1]-a[i])*(sum[i]+sum[n]-sum[j]),f[i+1][j][1]+(a[j]-a[i])*(sum[i]+sum[n]-sum[j]));
            f[i][j][1]=min(f[i][j-1][1]+(a[j]-a[j-1])*(sum[i-1]+sum[n]-sum[j-1]),f[i][j-1][0]+(a[j]-a[i])*(sum[i-1]+sum[n]-sum[j-1]));
        }
    }
    printf("%d",min(f[1][n][1],f[1][n][0]));
    return 0;
}

再来说一说消防局的设立,这道题的(题解)倒是给了一个很好地思路(我也不会做,只能看题解了)

一个贪心的思路,就是吗,面对一棵树,在一定范围内定点能覆盖更多的点,且答案不会更差

题的意思是找到最少的点覆盖整个图(不然家都烧没了消防队还没到)
面对这个距离2,我们可以通过一个点,一个点的父亲,还有他的爷爷来更新,当然要从最底层一直更新到整个树的根节点

设disifre[i]表示距离i节点最近的消防站距离是多少,如果大于2,就在爷爷的位置安放一个消防站,然后更新爷爷的父亲和爷爷的爷爷(脑子混乱)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2010;
int n;
int f[maxn];
int disfire[maxn];
int dis[maxn];
int id[maxn]; 
bool cmp(int x,int y){return dis[x]>dis[y];}
int ans;
int main()
{
    //freopen("input.txt","r",stdin);
//    freopen("output.txt","w",stdout);
    memset(disfire,127,sizeof(disfire));
    scanf("%d",&n);
    id[1]=1;
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&f[i]);
        dis[i]=dis[f[i]]+1;
        id[i]=i;
    }
    sort(id+1,id+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        int x=id[i],fa=f[x],gra=f[fa];
        disfire[x]=min(disfire[x],min(disfire[fa]+1,disfire[gra]+2));
        if(disfire[x]>2)
        {
            disfire[gra]=0;ans++;
            disfire[f[gra]]=min(disfire[f[gra]],1);
            disfire[f[f[gra]]]=min(disfire[f[f[gra]]],2);
        }
    }
    printf("%d",ans);
    return 0;
}

连图都没建,就把一道树形DP做出来了(开心)

但是我们只有这些还不够,我们可以拓展到距离为k

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;
const int maxn=2010;
int pre[maxn],last[maxn],other[maxn],l;
queue<int>q;
stack<int> s;
int n;
int fa[maxn];
void add(int x,int y)
{
    l++;
    pre[l]=last[x];
    last[x]=l;
    other[l]=y;
}
int vis[maxn];
void bfs()
{
    q.push(1);
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        if(vis[x]) continue;
        vis[x]=1;
        s.push(x);//保证深度小的先入站 
        for(int p=last[x];p;p=pre[p])
        {
            int v=other[p];
            if(vis[v]) continue;
            fa[v]=x;
            q.push(v);
        }
    }
}
int ans;

void dfs(int x,int dis)
{
    if(dis>2) return ;
    vis[x]=1;
    for(int p=last[x];p;p=pre[p])
    {
        int v=other[p];
        dfs(v,dis+1);//更新访问的点,已经有消防站覆盖 
    }
}

void go()
{
    memset(vis,0,sizeof(vis));
    while(!s.empty())
    {
        int x=s.top();
        s.pop();
        if(vis[x]) continue;
        ans++;dfs(fa[fa[x]],0);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        int ui;
        scanf("%d",&ui);
        add(i,ui);
        add(ui,i);
    }
    bfs();
    go();
    printf("%d",ans);
    return 0;0
}

至于大逃离,我也不知道为什么能这么做。。。

f[i][j][p][k],位置i,j,收集魔液差为p,k=0表示小a,k=1表示uim

因为k+1变成0,所以k++;

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mo=1e9+7;
const int maxn=810;
int f[maxn][maxn][20][2];
int n,m,k;
int ans;
int g[maxn][maxn];
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    k++;
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
    {
        scanf("%d",&g[i][j]);
        f[i][j][g[i][j]][0]=1;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            for(int p=0;p<=k;p++)
            {
            f[i][j][p][0]+=f[i-1][j][(p-g[i][j]+k)%k][1];
                f[i][j][p][0]%=mo;
            f[i][j][p][0]+=f[i][j-1][(p-g[i][j]+k)%k][1];
                f[i][j][p][0]%=mo;
            f[i][j][p][1]+=f[i-1][j][(p+g[i][j]+k)%k][0];
                f[i][j][p][1]%=mo;
            f[i][j][p][1]+=f[i][j-1][(p+g[i][j]+k)%k][0];
                f[i][j][p][1]%=mo;
            }
            ans+=f[i][j][0][1];
            ans%=mo;
        }
    }
    printf("%d",ans);
    return 0;
}

矩阵取数,每次都取每行的前一个或最后一个,每行互不影响,所以一行一行做就行了

因为我也不知道

猜你喜欢

转载自www.cnblogs.com/WHFF521/p/11508486.html