版权声明:欢迎评论交流,转载请注明原作者。 https://blog.csdn.net/m0_37809890/article/details/83280181
我有毒
总结
- 值域有限的情况下构造,枚举初始值总是一个好方法。
- 先想简单情况,然后把复杂情况转化成简单情况。
A. Golden Plate 计算
给h*w的网格图,把最外面一圈染成黄色,第三圈,第五圈…一共染k圈,求最后的黄色格子数量。
直接计算,这道题甚至保证k不会超界
int n=read(),m=read(),k=read();
int ans = 0;
while(k--)
{
ans += n*2+m*2-4;
n-=4;
m-=4;
}
printf("%d\n",ans );
B. Curiosity Has No Limits 构造
给定n,和长度为n-1的序列a,b(值域0,1,2,3),构造一个长度为n的序列t,使得
从值域入手,打表发现如果已知 ,那么 要么不存在,要么唯一确定。枚举 ,依次判断能否构造出来。
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 100016, MOD = 1000000007;
vector<int> save[4][4][4];
int sor[M],san[M],ans[M];
int main(void)
{
for(int num=0;num<4;++num)
{
for(int aor=0;aor<4;++aor)
{
for(int aan=0;aan<4;++aan)
{
for(int i=0;i<4;i++)
if(((num&i)==aan) && ((num|i)==aor))
save[num][aor][aan].push_back(i);
}
}
}
int n = read();
for(int i=1;i<n;++i)
sor[i] = read();
for(int i=1;i<n;++i)
san[i] = read();
int suc = 0;
for(ans[1]=0;ans[1]<4;++ans[1])
{
int fail=0;
for(int i=1;i<n;++i)
{
if(save[ ans[i] ][ sor[i] ][ san[i] ].size())
{
ans[i+1] = save[ ans[i] ][ sor[i] ][ san[i] ][0];
}
else
{
fail=1;
break;
}
}
if(!fail)
{
suc = 1;
printf("YES\n");
for(int i=1;i<=n;++i)
printf("%d ",ans[i] );
printf("\n");
break;
}
}
if(suc==0)
printf("NO\n");
return 0;
}
C. Cram Time 贪心
现在有无限本书可以读,读完第k本书需要花费k的时间。第一天有a时间,第二天有b时间,一本书不能在两天都读。问这两天最多能读完几本书,输出方案。
共a+b的时间,最多读前x本书,显然
。尽量填满a,然后剩下的给b即可。
一定可以填满a(或者剩下不超过
的空缺),填的方法是每次填当前的最大值或者剩下的值,写函数多余了.
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 500016, MOD = 1000000007;
int tag[M];
void fill(int n,int m)
{
if(n==0||m==0) return;
if(n<=m)
{
tag[n]=1;
}
else
{
tag[m]=1;
fill(n-m,m-1);
}
}
void print(const vector<int>&vi)
{
printf("%d\n",vi.size() );
for(auto x:vi)
printf("%d ",x );
printf("\n");
}
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
ll a = read(), b = read(), t;
for(t=0;t*(t+1)/2<=a+b;++t)
;
--t;
if(t==0)
{
printf("%d\n\n%d\n\n",0,0 );
}
else
{
fill(a,t);
vector<int> resa,resb;
for(int i=1;i<=t;++i)
(tag[i]?resa:resb).push_back(i);
print(resa);
print(resb);
}
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
D. Minimum path 贪心,DP
给定n(2000),k(n^2),以及n*n的字母矩阵。
最多改变k个位置上的字母,求一条从左上到右下的字典序最小的路径。
考虑没有改变操作时,按距左上角的距离进行遍历,维护每个位置是否是“好的”。
首先原点(左上角)是好的。如果一个距原点d的位置是好的,需要满足两个条件:
- 左边或上边的位置是好的
- 在所有距原点d且满足第一个条件的位置中,这个位置的字符是最小的。
终点(右下角)一定是好的,且一定至少有一条从原点到终点的全为好的路径,输出任意一条即可。
有改变操作时,就计算每个位置到左上角的最少的非a字符的数量(DP),把这个值不超过k的位置都改成a,再执行上述算法即可。
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 2048, MOD = 1000000007;
char save[M][M];
int dp[M][M];
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
int n = read(), k = read();
for(int i=1;i<=n;++i)
scanf("%s",save[i]+1);
memset(dp,0x3f,sizeof(dp));
dp[0][1] = 0;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
dp[i][j] = min(dp[i-1][j],dp[i][j-1])+(save[i][j]!='a');
if(dp[i][j]<=k) save[i][j] = 'a';
}
}
memset(dp,0,sizeof(dp));
dp[0][1] = 1; //许可
for(int dis=2;dis<=n<<1;++dis)
{
char mi = 'z';
for(int r=1;r<=n;++r) if(dis-r<=n && dis-r>=1)
{
int c = dis-r;
if(dp[r-1][c] || dp[r][c-1])
mi = min(mi,save[r][c]);
}
for(int r=1;r<=n;++r) if(dis-r<=n && dis-r>=1)
{
int c = dis-r;
if(dp[r-1][c] || dp[r][c-1])
if(save[r][c]==mi)
dp[r][c] = 1;
}
}
stack<char> st;
int r=n,c=n;
while(1)
{
st.push(save[r][c]);
if(r==1&&c==1) break;
dp[r-1][c]?--r:--c;
}
while(!st.empty())
{
putchar(st.top());st.pop();
}
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}