【HDOJ 】 String problem(最大权闭合图)

【HDOJ 】 String problem(最大权闭合图)

String problem

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 134    Accepted Submission(s): 61

Problem Description
This is a simple problem about string. Now a string S contains only ‘0’-‘9’. ?? wants to select a subsequence from this string. And makes this subsequence score maximum. The subsequence’s score is calculated as follows:
Score= Value – Total_Cost
The calculation of the Cost is as follows:
If the number of characters x in the subsequence is kx, And the two coefficients are ax,bx,The cost of character x calculated as follows:

{cost[x]=0,kx=0cost[x]=ax(kx1)+bx,kx0

TotalCost=9i=0cost[i]

The calculation of the Value is as follows:

Value=0;
for(int i=1;i<=length(substr);++i){
     for(int j=1;j<=length(substr);++j){
          if(i!=j)
              Value+=w[id[i]][id[j]];
     }
}

id[i] is the position of the subsequence’s ith character in the original string,for example,if the original string is “13579”,and the subsubquence is “159”,then the array id ={1,3,5}. The w is a weight matrix.

Input
The first line contains an integer T, denoting the number of the test cases.
For each test case, the first line contains one integers n, the length of a string.
Next line contains the string S.
Next ten lines,each line contains ai,bi,denote the char i’s(0-9) coefficients
Next is a n*n matrix w.
Limits:
T<=20,
0<=n<=100
0<=ai<=bi<=1000
0<=w[i][j]<=50

Output
Each test output one line “Case #x: y” , where x is the case number ,staring from 1. y is the Maximum score.

Sample Input
1
3
135
1 2
1 2
1 2
1 2
1 2
1 2
1 2
1 2
1 2
1 2
0 0 3
1 0 0
4 0 0

Sample Output
Case #1: 3

Hint
we can choose “15”,id[]={1,3} then Value=w[1][3]+w[3][1]=7,
Total_Cost=2+2=4,Score=7-4=3

Author
FZU

Source
2016 Multi-University Training Contest 4

题目大意:
给出一个由0~9组成的长为n的字符串。
之后10个a,b。
a[i]表示i的花费(0 <= i <= 9)
b[i]表示i的初次花费(0 <= i <= 9)
之后一个n*n的矩阵,表示 Pij 价值

题目要求从母串中找到一个子序列(可不连续)
子序列的价值为

Value=0;
for(int i=1;i<=length(substr);++i){
     for(int j=1;j<=length(substr);++j){
          if(i!=j)
              Value+=w[id[i]][id[j]];
     }
}

substr为找到的子序列。
id[i] 为序列中第i个字符在原串中的位置。
w[id[i]][id[j]] 为子序列中第i个字符与第j个字符映射到矩阵中的价值。
这样累加起来就是子序列的价值

子序列的花费为

{cost[x]=0,kx=0cost[x]=ax(kx1)+bx,kx0

TotalCost=9i=0cost[i]

x为0~9
kx为子序列中包含的x这个数字的个数。

找到最大的 valcost

最大权闭合图问题。。
赛后去补,知道了这种有制约关系的流量问题可以用最大权闭合图搞。
然后最大权闭合图问题转化为求最小割的问题。求最小割用最小割容量=最大流,即可将问题转化为求最大流的问题。

然后最大利益=所有点正权值之和-最小割。

至于证明……慢慢啃把=。=、

总之以后遇到类似的题应该会往那里想。

之后主要是建图。

这里就拿样例举个栗子
1
3
135
1 2
1 2
1 2
1 2
1 2
1 2
1 2
1 2
1 2
1 2
0 0 3
1 0 0
4 0 0
这里写图片描述
没安啥优美的作图工具。。。凑活看吧=。=、

大体方式就是源点与盈利连接
汇点与花费连接。
之后用最大权闭合图的那套证明,可得到
最大利益=所有点正权值之和-最小割(可用最大流求得)。

Pij 表示选择的串包含i->j这一关系,也就是包含原串中第i和第j个字母。如果要得到这一价值的话,就要连通si与sj。如果连通si,那么si这个数字就要有。这样就构成了这个图。
连通 Pij 的价值为 w[i][j]+w[j][i]
每选择一个数字(假设是’0’),就要亏损a[0],由于第一次亏损b[0].
因此’0’到源点价值为 b[0] 。但由于s中的数字0如果被选中,既走向汇点又走向’0’,因此’0’走向汇点的价值应为 (b[0]a[0]) (第一次亏损b[0])

代码如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 1e9+7;
const double eps = 1e-8;

struct Edge
{
    int v,w,next;
    Edge(){}
    Edge(int _v,int _w,int _next):v(_v),w(_w),next(_next){}
};

int head[233333];
Edge eg[2333333];
int tp;
int a[111],b[111];
int mat[111][111];
char str[1111];
int n,st,en;

void Add(int u,int v,int f)
{
    eg[tp] = Edge(v,f,head[u]);
    head[u] = tp++;
    eg[tp] = Edge(u,0,head[v]);
    head[v] = tp++;
}

int gap[233333];
int dis[233333];
int pre[233333];
int cur[233333];
int tot;

int sap()
{
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    memset(pre,-1,sizeof(pre));
    memset(cur,-1,sizeof(cur));

    gap[0] = n;
    int u,v,flow,ans;

    flow = INF;
    u = pre[0] = 0;
    ans = 0;

    while(dis[0] < en)
    {
        for(int &i = cur[u]; i != -1; i = eg[i].next)
        {
            if(eg[i].w && dis[eg[i].v]+1 == dis[u]) break;
        }


        if(cur[u] != -1)
        {
            v = eg[cur[u]].v;
            flow = min(flow,eg[cur[u]].w);
            pre[v] = u;
            u = v;

            if(u == en)
            {
                for(; u != 0; u = pre[u])
                {
                    eg[cur[pre[u]]].w -= flow;
                    eg[cur[pre[u]]^1].w += flow;
                }
                ans += flow;
                flow = INF;
            }
        }
        else
        {
            //printf("%d %d %d %d %d----\n",en,u,ans,tot,dis[0]);
            int mn = n;

            for(int i = head[u]; i != -1; i = eg[i].next)
            {
                //printf("%d %d %d\n",eg[i].v,eg[i].w,dis[eg[i].v]);
                if(eg[i].w && dis[eg[i].v] < mn) 
                {
                    mn = dis[eg[i].v];
                    cur[u] = i;
                }
            }

            //printf("----%d\n",gap[dis[u]]);
            if(!(--gap[dis[u]])) break;
            dis[u] = mn+1;
        //  printf("%d\n",mn+1);
            gap[dis[u]]++;
        //puts("1");
            u = pre[u];
        }
    }
    //printf("%d\n",ans);
    return ans;
}

#ifdef debug
bool vis[11111];
void prt(int u,int blank)
{
    vis[u] = 1;
    //printf("%d---",u);
    if(u == 0) puts("scr---");
    else if(u == en) puts("end---");
    else
    {
        if(u <= n) printf("pt%d",u);
        else if(u <= n*n+n) printf("pt%d %d",u%n? u/n: (u-1)/n,u%n? u%n: n);
        else printf("Num%d",u-(n*n)-n-1);
        puts("");
    }

    if(u == 315 || u == 17 || u == 4) 
    for(int i = head[u]; i != -1; i = eg[i].next)
    {
        int v = eg[i].v;
        if(v == 0) puts("scr");
        else if(v == en) printf("->end:%d\n",eg[i].w);
        else
        {
            if(v <= n) printf("->pt%d:%d",v,eg[i].w);
            else if(v <= n*n+n) printf("->pt%d %d:%d",v%n? v/n: (v-1)/n,v%n? v%n: n,eg[i].w);
            else printf("->Num%d:%d",v-(n*n)-n-1,eg[i].w);
            puts("");
        }
    }

    for(int i = head[u]; i != -1; i = eg[i].next)
    {
        if(vis[eg[i].v]) continue;
        prt(eg[i].v,blank+1);
    }
}
#endif

int solve()
{
    memset(head,-1,sizeof(head));
    tp = 0;
    tot = 0;

    st = 0;
    en = (n+1)*n+11;

    for(int i = 0; i <= 9; ++i) 
    {
        scanf("%d%d",&a[i],&b[i]);
        Add(i+(n+1)*n+1,en,b[i]-a[i]);
    }

    for(int i = 1; i <= n; ++i)
    {
        Add(i,str[i]-'0'+(n+1)*n+1,INF);
        Add(i,en,a[str[i]-'0']);
        for(int j = 1; j <= n; ++j)
        {
            scanf("%d",&mat[i][j]);
            if(i > j) 
            {
                Add(0,j*n+i,mat[i][j]+mat[j][i]);
                Add(j*n+i,i,INF);
                Add(j*n+i,j,INF);
            }
            tot += mat[i][j];
        }
    }

#ifdef debug
    memset(vis,0,sizeof(vis));
    prt(0,0);
#endif
    n = (n+1)*n+12;
    return tot-sap();
}

int main()
{
#ifdef debug
    fread("1009.in");
    //fwrite("");
#endif

    int t;

    scanf("%d",&t);

    for(int z = 1; z <= t; ++z)
    {
        scanf("%d%s",&n,str+1);

        printf("Case #%d: %d\n",z,solve());
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值