1 最大正方形
给一个N*N的01矩阵, 求一个面积最大的全为1的正方形子矩阵. 输出它的面积.
80%的数据中 N<=250;
100%的数据中 N <= 1000。
记录矩阵的前缀和,循环时只用 n 2 n^2 n2,再加 l o g n log n logn的二分找到前缀和最大的子矩阵,记录矩阵边长即为对应答案
#include <cstdio>
#include <algorithm>
using namespace std;
int n,ans;
int a[1005][1005];
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++){
char ch;
for (int j=1;j<=n;j++){
ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
a[i][j]=ch-'0';
}
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
for (int i=n;i>ans;i--){
for (int j=n;j>ans;j--){
int l=ans+1,r=min(i,j);
while (l<=r){
int mid=(l+r)/2;
if (a[i][j]-a[i-mid][j]-a[i][j-mid]+a[i-mid][j-mid]==mid*mid) l=mid+1,ans=mid;
else r=mid-1;
}
}
}
printf("%d",ans*ans);}
2 最大值
找到一个数组的最大值的一种方法是从数组开头从前到后对数组进行扫描,令max=a0,如果a[i]>max,就更新max,这样就可以在O(N)的时间里找到一个数组的最大值。
这个问题是相当简单的,但是想到了另一个问题,如果一个包含N个元素的数组a里面的元素的值是在1…K之间的整数,存在多少个不同的数组a,进行了如上扫描之后,max恰好进行了p次更新?
下面是N = 4,K = 3,P = 2时所有情况
-
{1,1,2,3}
-
{1,2,1,3}
-
{1,2,2,3}
-
{1,2,3,1}
-
{1,2,3,2}
-
{1,2,3,3}
共有6种情况
由于答案可能很大,所以你仅仅需要把答案mod 10^9+7输出。
1 <= T <= 10000 1 <= n <= 100 1 <= K <= 300 0 <= P < n
疲惫。。。
一个动态规划
是一个动态规划。
它是一个动态规划。。
f[i][j][k]表示第i位,到第i位为止出现的最大值为j,最大值更新了k次,的方案数
f[i][j][k]=f[i-1][q] (1<=q<j)[k-1]+f[i-1][j][k]*j
*j因为上一位为j时,当前位可以填1-j的任何数,所以方案数 *j
然后f[i-1][q] (1<=q<j)[k-1] 这一步可以用前缀和优化
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e9+7;
int t,n,k,p;
long long f[105][305][105];
long long s[105][305][105];
int main(){
for (int i=1;i<=300;i++){
f[1][i][0]=1;
s[1][i][0]=i%N;
}
for (int i=2;i<=100;i++){
for (int j=1;j<=300;j++){
f[i][j][0]=(f[i][j][0]%N+(f[i-1][j][0]%N)*(j%N))%N;
s[i][j][0]=(s[i][j][0]%N+f[i][j][0]%N+s[i][j-1][0]%N)%N;
for (int k=1;k<100;k++){
f[i][j][k]=(f[i][j][k]%N+(f[i-1][j][k]%N)*(j%N)+s[i-1][j-1][k-1]%N)%N;
s[i][j][k]=(s[i][j][k]%N+f[i][j][k]%N+s[i][j-1][k]%N)%N;
}
}
}
scanf("%d",&t);
for (;t;t--){
scanf("%d%d%d",&n,&k,&p);
printf("%lld\n",s[n][k][p]%N);
}
}
3 数字游戏
有这么一个游戏:
写出一个1~N的排列a[i],然后每次将相邻两个数相加,构成新的序列,再对新序列进行这样的操作,显然每次构成的序列都比上一次的序列长度少1,直到只剩下一个数字位置。下面是一个例子:
3 1 2 4
4 3 6
7 9
16
最后得到16这样一个数字。
现在想要倒着玩这样一个游戏,如果知道N,知道最后得到的数字的大小sum,请你求出最初序列a[i],为1~N的一个排列。若答案有多种可能,则输出字典序最小的那一个。
预处理出杨辉三角的值,如
1 2 1
1 1
1
作为对每一个的权值,然后搜索全排列,剪枝,就,就过了(?)
嗯
#include <cstdio>
#include <cstring>
using namespace std;
int n,sum;int c[25],a[25];
int f[25][25];int b[25];
bool dfs(int d,int s){
if (s>sum) return 0;
if (d>n){
if (s==sum) return 1;
return 0;
}
for (int i=1;i<=n;i++)
if (!b[i]){
b[i]=1;
a[d]=i;
if (dfs(d+1,s+c[d]*i)) return 1;
b[i]=0;
}
return 0;
}
int main(){
scanf("%d%d",&n,&sum);
for (int i=1;i<=n;i++){
memset(f,0,sizeof f);
f[1][i]=1;
for (int j=2;j<=n;j++)
for (int k=1;k<=n-j+1;k++)
f[j][k]=f[j-1][k]+f[j-1][k+1];
c[i]=f[n][1];
}
dfs(1,0);
for (int i=1;i<=n;i++)
printf("%d ",a[i]);
}
4 家庭作业
老师在开学第一天就把所有作业都布置了,每个作业如果在规定的时间内交上来的话才有学分。每个作业的截止日期和学分可能是不同的。例如如果一个作业学分为10,要求在6天内交,那么要想拿到这10学分,就必须在第6天结束前交。
每个作业的完成时间都是只有一天。例如,假设有7次作业的学分和完成时间如下:
作业号 1 2 3 4 5 6 7
期限 1 1 3 3 2 2 6
学分 6 7 2 1 4 5 1
最多可以获得15学分,其中一个完成作业的次序为2,6,3,1,7,5,4,注意可能d还有其他方法。
你的任务就是找到一个完成作业的顺序获得最大学分。
竟然可以用贪心…可以用贪心…用贪心…贪心…贪…
然而是用链表优化的贪心
以学分为关键字排序,对于未选作业中学分最大的,给它安排它的期限前的最晚空闲天,这样可以尽可能得多分
找最晚空闲天可以用链表记录每一天前最晚的空闲天并随时更新
#include <cstdio>
#include <algorithm>
using namespace std;
int n;long long ans;
int l[700005],tot;
int b[700005];
struct node{
int t,w;
}a[1000006];
bool comp(node a,node b)
{
if (a.w>b.w) return 1;
return 0;
}
int dfs(int x){
if (b[x]) return x;
l[x]=dfs(l[x]);
return l[x];
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++){
scanf("%d%d",&a[i].t,&a[i].w);
if (a[i].t>tot) tot=a[i].t;
}
sort(a+1,a+1+n,comp);
for (int i=0;i<=tot;i++){
l[i]=i-1;
b[i]=1;
}
for (int i=1;i<=n;i++){
int tt=dfs(a[i].t);
if (tt!=0) {
ans+=a[i].w;
b[tt]=0;
if (tt!=a[i].t)
l[a[i].t]=l[tt];
}
}
printf("%lld",ans);
}
经年痴心妄想,一朝走火入魔
正经句子,相信我
形容有朝一日夙愿得成,梦想成真