C. Dungeons and Candies (Zepto Code Rush 2014 最小生成树)

C. Dungeons and Candies
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

During the loading of the game "Dungeons and Candies" you are required to get descriptions of k levels from the server. Each description is a map of an n × m checkered rectangular field. Some cells of the field contain candies (each cell has at most one candy). An empty cell is denoted as "." on the map, but if a cell has a candy, it is denoted as a letter of the English alphabet. A level may contain identical candies, in this case the letters in the corresponding cells of the map will be the same.

When you transmit information via a network, you want to minimize traffic — the total size of the transferred data. The levels can be transmitted in any order. There are two ways to transmit the current level A:

  1. You can transmit the whole level A. Then you need to transmit n·m bytes via the network.
  2. You can transmit the difference between level A and some previously transmitted level B (if it exists); this operation requires to transmit dA, B·w bytes, where dA, B is the number of cells of the field that are different for A and B, and w is a constant. Note, that you should compare only the corresponding cells of levels A and B to calculate dA, B. You cannot transform the maps of levels, i.e. rotate or shift them relatively to each other.

Your task is to find a way to transfer all the k levels and minimize the traffic.

Input

The first line contains four integers n, m, k, w (1 ≤ n, m ≤ 10; 1 ≤ k, w ≤ 1000). Then follows the description of k levels. Each level is described by n lines, each line contains m characters. Each character is either a letter of the English alphabet or a dot ("."). Please note that the case of the letters matters.

Output

In the first line print the required minimum number of transferred bytes.

Then print k pairs of integers x1, y1, x2, y2, ..., xk, yk, describing the way to transfer levels. Pair xiyi means that level xi needs to be transferred by way yi. If yi equals 0, that means that the level must be transferred using the first way, otherwise yi must be equal to the number of a previously transferred level. It means that you will transfer the difference between levels yi and xi to transfer level xi. Print the pairs in the order of transferring levels. The levels are numbered 1 through k in the order they follow in the input.

If there are multiple optimal solutions, you can print any of them.

Sample test(s)
input
2 3 3 2
A.A
...
A.a
..C
X.Y
...
output
14
1 0
2 1
3 1
input
1 1 4 1
A
.
B
.
output
3
1 0
2 0
4 2
3 0
input
1 3 5 2
ABA
BBB
BBA
BAB
ABB
output
11
1 0
3 1
2 3
4 2
5 1

题意: 
k
个点,每个点都是一个n * mchar型矩阵。对与每个点,权值为n * m或者找到一个之前的点,取两个矩阵对应位置不同的字符个数乘以w。找到一个序列,使得所有点的权值和最小并输出。


代码:


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#pragma comment (linker,"/STACK:102400000,102400000")
#define maxn 1005
#define MAXN 2005
#define mod 1000000009
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-6
typedef long long ll;
using namespace std;

int n,m,k,w;
int dist[maxn];
char MP[maxn][11][11];
int mp[maxn][maxn];
int pre[maxn];
int path[maxn][2];  //记录路径
int vis[maxn];
int num;

int Different(char a[11][11],char b[11][11])//求两个矩阵不同的个数
{
    int cnt=0;
    for (int i=0;i<n;i++)
        for (int j=0;j<m;j++)
            if (a[i][j]!=b[i][j])
                cnt++;
    return cnt;
}

int prim()  //prim算法求最小生成树
{
    int pos,ans=0,minn;
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    for (int i=1;i<=k;i++) dist[i]=n*m;
    num=0;
    for(int i=1;i<=k;i++)
    {
        minn=INF;
        pos=-1;
        for(int j=1;j<=k;j++)
        {
            if(!vis[j]&&minn>dist[j])
            {
                pos=j;
                minn=dist[j];
            }
        }
        ans+=dist[pos];
        vis[pos]=1;
        if(pos==-1)
            break;
        if (dist[pos]==n*m) //这种情况下直接传输过去花费较小
        {
            path[num][0]=pos;
            path[num++][1]=0;
        }
        else    //否则通过之前的已经传输过去的将它传过去
        {
            path[num][0]=pos;
            path[num++][1]=pre[pos];
        }
        for(int j=1;j<=k;j++)
        {
            if(!vis[j]&&dist[j]>mp[pos][j])
            {
                dist[j]=mp[pos][j];
                pre[j]=pos;
            }
        }
    }
    return ans;
}

int main()
{
    int i,j;
    while (~scanf("%d%d%d%d",&n,&m,&k,&w))
    {
        for (i=1;i<=k;i++)
        {
            for (j=0;j<n;j++)
                scanf("%s",MP[i][j]);
        }
        for (i=1;i<=k;i++)  //建图
        {
            for (j=i+1;j<=k;j++)
                mp[i][j]=mp[j][i]=min(n*m,Different(MP[i],MP[j])*w);
            mp[i][i]=0;
        }
        printf("%d\n",prim());  //输出最小花费
        for (i=0;i<num;i++) //输出方案
            printf("%d %d\n",path[i][0],path[i][1]);
    }
    return 0;
}




  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值