单词矩阵(twofive.pas/c/cpp Time:1s Memory:256M)
问题描述
LJY开始研究英语的单词。对于包含字母A到Y各一次的单词S,将其从上到下从左到右写在一个5*5的矩阵中,如单词ADJPTBEKQUCGLRVFINSWHMOXY写出来如下:
合法 | 不合法 |
---|---|
A D J P T | |
B E K Q U | B E G Q U |
C G L R V | C K L R V |
F I N S W | F I N S W |
H M O X Y | H M O X Y |
若该矩阵满足每一行每一列的字母都是字典序递增的则满足LJY对优美的要求,如上述单词就是优美的,而ADJPTBEGQUCKLRVFINSWHMOXY则不是(第二列不满足要求)。
LJY将所有优美的单词按字典序列出,从小到大编号1,2,……
请你完成以下两种任务:
1. 给定一个优美的单词,求其编号。
2. 给定一个编号,求对应的优美的单词。
输入
输入文件名为twofive.in。
输入第一行一个字母,W表示任务1,N表示任务2。若是任务1,第二行是一个优美的单词,否则第二行是一个正整数,表示某个优美的单词的编号,保证该数不超过优美的单词的总数。
输出
输出文件名为twofive.txt。
输出仅一行,若是任务1,输出对应编号,否则输出对应的优美的单词
输入样例1
W
ABCDEFGHIJKLMNOPQRSUTVWXY
输出样例1
2
输入样例2
N
20
输出样例1
ABCDEFGHIJKLMNOPQSUWRTVXY
数据范围
保证数据合法。保证数据有梯度。
分析
搜索
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int sm = 36;
int opr,n,now,ans[sm];
char ch,s[sm];
int f[6][6][6][6][6],used[sm];
inline bool check(int x,int y) { return ans[x]?ans[x]==y:1; }
inline int dfs(int a,int b,int c,int d,int e,int tot) {
if(tot==25)return 1;
int &tmp=f[a][b][c][d][e];
if(tmp)return tmp;
if(a<5&&check(a,tot+1)) tmp+=dfs(a+1,b,c,d,e,tot+1);
if(b<a&&check(b+5,tot+1)) tmp+=dfs(a,b+1,c,d,e,tot+1);
if(c<b&&check(c+10,tot+1))tmp+=dfs(a,b,c+1,d,e,tot+1);
if(d<c&&check(d+15,tot+1))tmp+=dfs(a,b,c,d+1,e,tot+1);
if(e<d&&check(e+20,tot+1))tmp+=dfs(a,b,c,d,e+1,tot+1);
return tmp;
}
int main() {
freopen("twofive.in","r",stdin);
freopen("twofive.out","w",stdout);
ch=getchar();
if(ch=='W') {
n=0;
scanf("%s",s);
for(int i=0;i<25;++i){
for(ans[i]=1;ans[i]<s[i]-'A'+1;++ans[i]) {//
if(used[ans[i]])continue;
memset(f,0,sizeof(f));
n+=dfs(0,0,0,0,0,0);
}
used[ans[i]]=1;
}
printf("%d\n",n+1);
}
else {
scanf("%d",&n);
for(int i=0;i<25;++i) {
for(ans[i]=1;ans[i]<=26;++ans[i]) {
if(used[ans[i]])continue;
memset(f,0,sizeof(f));
now=dfs(0,0,0,0,0,0);
if(now>=n)break;n-=now;//
}
used[ans[i]]=1;
}
for(int i=0;i<25;++i)putchar(ans[i]+'A'-1);
printf("\n");
}
return 0;
}
道路修建(Road.pas/c/cpp Time:1.2s Memory:256M)
问题描述
现在在 LJY 星球上有 N 个城市。LJY 为了使各个国家的经济发展,决定在各个国家之间建设双向道路使得国家之间连通。但是 LJY 很吝啬,只愿意修建恰好 n–1 条双向道路。每条道路的修建都要付出一定的费用,这个费用等于道路长度乘以道路两端的国家个数之差的绝对值。例如,在下图中,虚线所示道路两端分别有 2 个、4 个国家,如果该道路长度为 1,则费用为 1×|2 – 4|=2。图中圆圈里的数字表示国家的编号。
由于国家的数量十分庞大,道路的建造方案有很多种,同时每种方案的修建费用难以用人工计算,LJY 决定找人设计一个软件,对于给定的建造方案,计算出所需要的费用。请你帮助 LJY 设计一个这样的软件。
输入
输入文件名为 Road.in。输入第一行为一个正整数 N,代表总的点数的个数。下接 N-1 行,每行三个正整数 ai、bi、ci,代表有一条长度为 ci 的双向道路连接 ai 和 bi 两个国家之间。
输出
输出文件名为 Road.txt。输出一行一个正整数,为修建所有道路的总费用。
输入样例
6
1 2 1
1 3 1
1 4 2
6 3 1
5 2 1
输出样例
20
数据范围
对于 40%的数据,N<=1000。
对于 70%的数据,N<=100000。
对于 100%的数据,N<=1000000。
不允许开开关。
分析
类拓扑排序地使用队列,叶节点入队,cnt记直接或间接连的点数
代码
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int sm = 1e6+10;
int s=1,n,x,y,k;
ll ans,z;
int ex[sm*2],mk[sm],rd[sm],head[sm],cnt[sm];
struct edge {
int to,nxt;
ll len;
}e[sm*2];
queue<ll>q;
void build(int x,int y,ll z) {
e[++s].to=y;
e[s].len=z;
e[s].nxt=head[x];
head[x]=s;
rd[y]++;
}
int main() {
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<n;++i){
scanf("%d%d%lld",&x,&y,&z);
build(x,y,z);
build(y,x,z);
}
for(int i=1;i<=n;++i)
if(rd[i]==1) {
q.push(i);
rd[i]=-1;
mk[i]=1;
}
while(!q.empty()) {
k=q.front();
q.pop();
for(int i=head[k];i;i=e[i].nxt) {
if(!ex[i]) {
ex[i]=1;ex[i^1]=1;
rd[e[i].to]--;cnt[e[i].to]+=cnt[k]+1;
if(rd[e[i].to]==1&&!mk[e[i].to]) {
q.push(e[i].to);
rd[e[i].to]=-1;
mk[e[i].to]=1;
}
ans+=e[i].len*abs(n-2*(cnt[k]+1));
}
}
}
printf("%lld\n",ans);
return 0;
}
迷宫巡回(maze.pas/c/cpp Time:1s Memory:256M)
问题描述
现在有一个 N*M 的迷宫,LJY 处在第一行第一列这个位置,也就是起点上,迷宫的补给点在(N,M)。这个迷宫中的每个格子都有一个激情度,也就是说,LJY 走到这个格子便可以获得这个格子上的激情度。但是,走过一遍的格子便没有激情度了。所以,LJY 为了获得最大的激情度,便不希望走到同一个格子上,除了起点。首先,LJY 会从起点走到补给点,此时,LJY 只能向下或者向右运动到相邻的格子。到了补给点之后,LJY 又从补给点开始,向上或者向左运动到相邻的格子,一直到起点。当然,万一 LJY 走到了迷宫之外,他就挂定了,所以他绝对不会走到迷宫之外的。现在 LJY 想知道,自己巡回一遍迷宫之后,能获得的最大的激情度有多少?
输入
输入文件名为 maze.in。输入一行两个正整数 N 和 M,代表迷宫的行数和列数。下接一个 N 行 M 列的矩阵,其中第 I 行第 J 列的数代表游历这个格子的激情度。
输出
输出文件名为 maze.txt。输出一行一个正整数,代表 LJY 巡回一遍迷宫能获得的最大的激情度。
输入样例
3 3
0 3 9
2 8 5
5 7 0
输出样例
34
数据范围
对于 30%的数据,1<=N,M<=10。
对于 100%的数据,1<=N,M<=50。
保证每个格子的激情度均为不大于 100 的非负整数。
分析
斜线动归
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int sm = 55;
int st,en,u,n,m;
ll sum,w[sm][sm],f[sm*2-1][sm][sm];
int main() {
freopen("maze.in","r",stdin);
freopen("maze.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
scanf("%lld",&w[i][j]);
u=w[1][1]+w[n][m];
w[1][1]=w[n][m]=0;
for(int i=1;i<=n+m-1;++i)
{
st=i<=n?1:(i-n+1);
en=i<=n?i:(min(i,m));
for(int j=st;j<=en;++j)
for(int k=st;k<=en;++k){
if((i>1&&i<n+m-1)&&j==k)continue;
sum=w[i-j+1][j]+w[i-k+1][k];
if(i-1>=1) {
f[i][j][k]=max(f[i][j][k],f[i-1][j][k]+sum);
if(j-1>=1&&j-1!=k)f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k]+sum);
if(k-1>=1&&j!=k-1)f[i][j][k]=max(f[i][j][k],f[i-1][j][k-1]+sum);
if(j-1>=1&&k-1>=1)f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k-1]+sum);
}
}
}
printf("%lld\n",f[n+m-1][m][m]);
return 0;
}