8.9 纪中集训 Day9
T1粉刷匠
Description
每条木板被分为 M 个格子。
每个格子要被刷成红色或蓝色。
windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。
每个格子最多只能被粉刷一次。
如果windy只能粉刷 T 次,他最多能正确粉刷多少格子?
一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。
Input
接下来有N行,每行一个长度为M的字符串,'0'表示红色,'1'表示蓝色。
Output
Sample Input
3 6 3 111111 000000 001100
Sample Output
16
Hint
考场思路/正解
一眼看去感觉就是DP,于是就开始疯狂地推状态,等推出来后已经过了一个半小时了(太弱,太弱)。
细读题目,由于分为n块木板,所以我们可以将每块木板分开处理。通过博主一系列严密的推算,结合日象和方位等玄学,认为可设f[i][j],表示每一行到第i个数粉刷j次的最优解,zx[i][j]表示到第i行粉刷j次的最优解。从而便可推出方程式(具体方程式见程序)。
Code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,t,ans; int w[55][55],f[55][2525]/*f[i][j]表示每一行到第i个数粉刷j次的最优解*/,zx[55][2525]/*zx[i][j]表示到第i行粉刷j次的最优解*/; char ch[55][55]; int main() { scanf("%d%d%d",&n,&m,&t); for(int i=1;i<=n;i++) scanf("%s",ch[i]+1); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) w[i][j]=w[i][j-1]+ch[i][j]-'0'; for(int l=1;l<=n;l++) { memset(f,0,sizeof(f)); for(int i=1;i<=m;i++) for(int j=1;j<=min(m,t);j++) for(int k=1;k<=i;k++) f[i][j]=max(f[i][j],f[k-1][j-1]+max(w[l][i]-w[l][k-1],i-(k-1)-(w[l][i]-w[l][k-1]))); for(int i=1;i<=t;i++) for(int j=1;j<=min(i,m);j++) zx[l][i]=max(zx[l][i],zx[l-1][i-j]+f[m][j]); } for(int i=1;i<=t;i++) ans=max(ans,zx[n][i]); printf("%d",ans); return 0; }
T2迷路
Description
该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1。
现在给出该有向图,你能告诉windy总共有多少种不同的路径吗?
注意:windy不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。
Input
接下来有 N 行,每行一个长度为 N 的字符串。
第i行第j列为'0'表示从节点i到节点j没有边。
为'1'到'9'表示从节点i到节点j需要耗费的时间。
Output
Sample Input
2 2 11 00
Sample Output
1
Hint
考场思路
毒啊,虽然考试时候没有打,但是认为应该是用SPFA等最短路算法然后再一堆剪枝,结果考试出来看了正解发现与此没有太大关系。
正解
用矩阵乘法暴力三重for去算方案数,可是如何处理边权不等的问题呢?观察数据范围,边权的取值只为0~9,点数n<=10,所以我们可以暴力拆点,将其拆成边权为一的链,然后题目就解决了。不过由于第一次接触矩阵乘法求方案数所以改的有点久(菜啊)。
Code
#include<cstdio> #include<algorithm> #include<cstring> #define Mod 2009 using namespace std; int n,t; char ch[202]; struct Pike { int Map[202][202]; }a,ans; Pike operator * (const Pike a,const Pike b) { Pike c; memset(c.Map,0,sizeof(c.Map)); for(int i=1;i<=n*9;i++) for(int j=1;j<=n*9;j++) for(int k=1;k<=n*9;k++) c.Map[i][j]=(c.Map[i][j]+a.Map[i][k]*b.Map[k][j]%Mod)%Mod; return c; } void Quick(Pike s,int t) { while(t) { if(t&1) ans=ans*s; s=s*s; t>>=1; } } int main() { scanf("%d%d",&n,&t); for(int i=1;i<=n;i++) for(int j=1;j<=8;j++) a.Map[(i-1)*9+j][(i-1)*9+j+1]=1; for(int i=1;i<=n;i++) { scanf("%s",ch+1); for(int j=1;j<=n;j++) if(ch[j]>'0') a.Map[(i-1)*9+ch[j]-(int)'0'][9*(j-1)+1]=1; } for(int i=1;i<=n*9;i++) ans.Map[i][i]=1; Quick(a,t); printf("%d",ans.Map[1][(n-1)*9+1]); return 0; }
T3游戏
Description
windy学会了一种游戏。 对于1到N这N个数字,都有唯一且不同的1到N的数字与之对应。 最开始windy把数字按顺序1,2,3,……,N写一排在纸上。 然后再在这一排下面写上它们对应的数字。 然后又在新的一排下面写上它们对应的数字。 如此反复,直到序列再次变为1,2,3,……,N。 如: 1 2 3 4 5 6 对应的关系为 1->2 2->3 3->1 4->5 5->4 6->6 windy的操作如下
1 2 3 4 5 6
2 3 1 5 4 6
3 1 2 4 5 6
12 3 5 4 6
2 3 1 4 5 6
3 1 2 5 4 6
1 2 3 4 5 6
这时,我们就有若干排1到N的排列,上例中有7排。 现在windy想知道,对于所有可能的对应关系,有多少种可能的排数。
Input
一个整数,N。
Output
一个整数,可能的排数。
Sample Input
3
Sample Output
3
Hint
100%的数据,满足 1 <= N <= 1000 。
考场思路
没有去想,所以没有思路(理直气壮)。
正解
正解我认为这是玄学,题解写的十分清楚我就不乱扯了。
题解:
Code
#include<cstdio> #include<algorithm> #define LL long long using namespace std; LL n,sl,ans; LL book[1010],zs[1010]; LL f[1010]; void Make_zs() { for(LL i=2;i<=n;i++) { if(!book[i]) zs[++sl]=i; for(LL j=i+i;j<=n;j+=i) book[j]=1; } } int main() { scanf("%lld",&n); Make_zs(); f[0]=1; for(LL i=1;i<=sl;i++) for(LL j=n;j>=zs[i];j--) for(LL k=zs[i];k<=j;k*=zs[i]) f[j]+=f[j-k]; for(LL i=0;i<=n;i++) ans+=f[i]; printf("%lld",ans); return 0; }
T4windy数
Description
不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。
windy想知道,在A和B之间,包括A和B,总共有多少个windy数?
Input
Output
Sample Input
1 10
Sample Output
9
Hint
考场思路/正解
听别人说是数位DP,可我不会啊!于是我开始瞎搞,结果就错了。
考后改题,发现这还真是数位DP的模板题啊。
Code
#include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; LL a,b,suma,sumb; LL wz[11]; LL f[11][11]; //f[i][j]表示长度位i且最高位为j的windy数 LL Work(LL x) { LL sum=0,g=x,sl=0; while(g/10!=0) { wz[++sl]=g%10; g/=10; } wz[sl+1]=g; for(int i=1;i<=sl;i++) for(int j=1;j<=9;j++) sum+=f[i][j]; sl++; for(int i=1;i<g;i++) sum+=f[sl][i]; for(int i=sl-1;i>=1;i--) { for(int j=0;j<wz[i];j++) { if(abs(wz[i+1]-j)>=2) sum+=f[i][j]; } if(abs(wz[i+1]-wz[i])<2) break; } return sum; } int main() { scanf("%lld%lld",&a,&b); for(int i=0;i<=9;i++) f[1][i]=1; for(int i=2;i<=10;i++) for(int j=0;j<=9;j++) for(int k=0;k<=9;k++) if(abs(k-j)>=2) f[i][j]+=f[i-1][k]; printf("%lld",Work(b+1)-Work(a)); return 0; }
总结
今天考试,三题DP,一题矩阵乘法,其中有三题是省选难度的,你要我命啊!!
觉得自己打码速度不够快,考试时智商为0。