P1387 最大正方形
一道提高减的题目,但是做法却比较朴素,遍历找最大值就能得出结果,看了题解的dp和各种前缀和做法顿时感觉自愧不如,不过能做出来的方法就是好方法,下面是我的代码
#include<bits/stdc++.h>
using namespace std;
int a[109][109];
int ans;
void find(int x,int y){
int cnt=1;
while(1){
for(int i=x;i<=x+cnt;i++){
for(int j=y;j<=y+cnt;j++){
if(a[i][j]==0)goto loop;
}
}
cnt++;
}
loop:
ans=max(ans,cnt);
}
int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]==1){
find(i,j);
}
}
}
printf("%d",ans);
return 0;
}
P1934 封印
一道动态规划的题目
由于题目具有无后效性,所以想到用DP来解决。
我们令f[i]表示打破前i层封印消耗元气的最小值,则状态转移方程如下:
f[i]=max{f[i−1]+a[i]*n^2,f[k]+(a[k+1]+a[i])*sum(k+1,i)|0<k+1<i,a[k+1]+a[i]≤k}
状态转移方程写好后,问题在于求sum(k+1,i)时如果遍历一遍需要O(n)的复杂度。这样总复杂度为k(n^3),50-70分。
这个复杂度可以用预处理前缀和的方法来优化。用S[i]表示从a[1]到a[i]的
总和,则sum(k+1,i)=S[i]-S[k]。这样总复杂度为k(n^2),可以通过所有测试点。
感觉是一道很入门的动态规划,但我也花了不少时间才搞定
#include<bits/stdc++.h>
using namespace std;
long long a[1009],b[1009],dp[1009];
int main(){
int n,m;
scanf("%d %d",&n,&m);
int k=n*n;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
b[i]=b[i-1]+a[i];
}
for(int i=1;i<=n;i++){
long long ans=k*a[i]+dp[i-1];
for(int j=1;j<i;j++){
if(a[i]+a[j]>m)continue;
ans=min(ans,(a[i]+a[j])*(b[i]-b[j-1])+dp[j-1]);
}
dp[i]=ans;
}
printf("%lld",dp[n]);
return 0;
}
P3367 【模板】并查集
并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。 (摘自百度)
关于并查集和路径压缩:
现在我们假定 f[i]
表示第 i 个人的老大是谁。
现在我们有甲,乙,丙三个人(分别用 a, b, c 表示)
假设甲和乙打架了,甲做了丙的小弟。则有 f[a]=b
,
后来甲打赢了丙
那么丙就是甲的小弟了。有 f[c]=a
,
但是如果我们这样表示,丙不能直接知道甲
所以,我们必须直接让丙的大哥变成最大的老大。
这样说应该就能非常好理解并查集了
#include<bits/stdc++.h>
using namespace std;
int f[100009];
int find(int s){
if(f[s]==s)return s;
return f[s]=find(f[s]);
}//并查集的精华(找老大)
int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
f[i]=i;
}
int a,b,c;
for(int i=1;i<=m;i++){
scanf("%d %d %d",&a,&b,&c);
if(a==1){
f[find(b)]=find(c);
}
else{
if(find(b)==find(c))printf("Y\n");
else printf("N\n");
}
}
return 0;
}
P1455 搭配购买
我们发现这道题背包的限制条件很简单,买了A就必须买B,而且关系是互相的(而不是买A必买B,买B不一定买A)
所以啊,我们就用并查集将“有关系”的物品合成一个大大的物品,然后一个基础的01背包DP。
找错的过程也是十分痛苦,弄了我一个小时。
#include<bits/stdc++.h>
using namespace std;
int f[10009],dp[5000001];
struct node{
int cost;
int value;
};
node a[10009];
int find(int k){
if(f[k]==k)return k;
return f[k]=find(f[k]);
}
int main(){
int n,m,w;
scanf("%d %d %d",&n,&m,&w);
for(int i=1;i<=n;i++){
scanf("%d %d",&a[i].cost,&a[i].value);
}
for(int i=1;i<=n;i++){
f[i]=i;
}
int x,y;
for(int i=1;i<=m;i++){
scanf("%d %d",&x,&y);
f[find(x)]=find(y);
}
for(int i=1;i<=n;i++){
if(f[i]!=i){
a[find(i)].cost+=a[i].cost;
a[find(i)].value+=a[i].value;
a[i].cost=0;
a[i].value=0;
}
}
for(int i=1;i<=n;i++){
for(int j=w;j>=a[i].cost;j--){
dp[j]=max(dp[j],dp[j-a[i].cost]+a[i].value);
}
}
printf("%d",dp[w]);
return 0;
}