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
#include
#include
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
#include
#include
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
#include
#include
#include
#include
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
#include
#include
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;
}
矩阵取数,每次都取每行的前一个或最后一个,每行互不影响,所以一行一行做就行了
因为我也不知道在哪里结束的,所以就打个擂台吧
#include
#define ll __int128
using namespace std;
inline int read()
{
int X=0,w=1;
char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
return X*w;
}
ll ans;
ll n,m;
ll a[100][100];
ll base[100];
ll f[100][100];
void print(ll x)
{
if(!x) return;
if(x) print(x/10);
putchar(x%10+'0');
}
int main()
{
n=read();m=read();
base[0]=1;
for(int i=1;i<=m;i++)
{
base[i]=base[i-1]*2;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
a[i][j]=read();
}
}
for(int k=1;k<=n;k++)
{
memset(f,0,sizeof(f));
ll sum=0;
for(int l=1;l<=m;l++)
{
for(int r=m;r>=1;r--)
{
f[l][r]=max(f[l][r],f[l-1][r]+base[m-(r-l+1)]*a[k][l-1]);
f[l][r]=max(f[l][r],f[l][r+1]+base[m-(r-l+1)]*a[k][r+1]);
sum=max(sum,f[l][r]);
}
}
ans+=sum;
}
if(!ans) printf("0");
else print(ans);
return 0;
}