【DP】TEST20170429

10 篇文章 0 订阅

单词矩阵(twofive.pas/c/cpp Time:1s Memory:256M)

问题描述

    LJY开始研究英语的单词。对于包含字母A到Y各一次的单词S,将其从上到下从左到右写在一个5*5的矩阵中,如单词ADJPTBEKQUCGLRVFINSWHMOXY写出来如下:

合法不合法
A D J P T
B E K Q UB E G Q U
C G L R VC K L R V
F I N S WF I N S W
H M O X YH 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值