hdu 5772 String Problem

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=i=09cost[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
题意:给你一个包含0-9的序列,如果你选择了这个序列的第i位和第j为,那么你能得到w[i][j]+w[j][i]的收益,然后对于你选择的那些数字是有一定的花费的,花费为a[i]*(k-1)+b[i],a[i]和b[i]是数字i的两个参数,k代表数字i选了多少次。

思路如下:

先考虑这道题假设花费是固定的(对于第i位只和i有关)该怎么办,如果是固定的话那么就相当于如果我取w[i][j]和w[j][i]就必须花费i和j,这就是一道简单的最大闭合子图了,因此我们可以尝试考虑把花费变成像最大闭合子图的花费即可。
转变方式如下:
a[i]*(k-1)+b[i]可以转化为:a[i]*k+b[i]-a[i];
那么我们可以这么理解花费:
对于a[i]*k而言我们可以认为当选择了序列中的第i为,就花费a[num[i]]的。这一部分就等价了(等价的意思是说:以a[i]*k的形式来计算10个数字(0-9)各选择了几个和统计选取的序列的数字的a[num[i]]的和是相等的),然后我们可以再连一条从i到数字a[i]的边,流量为无穷大,再把a[i]连向汇点,流量为b[i]-a[i],代表如果选择了数字a[i]就花费b[i]-a[i]。(之所以要这么做是因为这样可以保证无论有多少个数字a[i],因为a[i]到汇点的边只有一条,使b[i]-a[i]只会被计算一次)

具体建边方式如下:

1.让s连向得分矩阵的每一位,流量为w[i][j];然后让这个矩阵的每一位连向i和j,流量为无穷大,代表如果想得到w[i][j]的得分,必须得选序列的i位和j位
2.让序列中的每一位都连向终点,流量为a[i],代表如果选择了第i位,就必须付出a[i]的代价
3。同时让序列中的每一位都连向它对应的那个数字所在的点,然后流量为无穷大,那个数字所在的点连向终点,流量为b[i]-a[i],代表如果选了数字i,那么必须付出b[i]-a[i]的代价

我们举个例子吧
假设序列为
12
a[1]=1,b[1]=2;
a[2]=10,b[2]=20;
得分矩阵为
0 100
1000 0

那么建的图大概是这样的


代码如下,(因为细节比较多,因此我把我所遇到的bug或者不好都写到了注释里)
#include <bits/stdc++.h>
using namespace std;
#define inf 1000000007
const int maxn = 100005;
struct node{
    int next,to,flow;
};
node edge[maxn];
int cnt,head[maxn],a[20],b[20],value[105][105],ans,E,n;
//value代表得分矩阵
char s[1000];
void init(){
    cnt=0;
    ans=0;
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=0;i<10;i++){
        scanf("%d%d",&a[i],&b[i]);
    }
    memset(value,0,sizeof(value));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&value[i][j]);
        }
    }
}

void add(int s,int e,int f){
    edge[cnt].flow=f;
    edge[cnt].to=e;
    edge[cnt].next=head[s];
    head[s]=cnt++;

    edge[cnt].flow=0;
    edge[cnt].to=s;
    edge[cnt].next=head[e];
    head[e]=cnt++;
}
void build(){
    int tot=1;
    E=n*n+10+1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i==j)continue;
            ans+=value[i][j];
            add(0,tot,value[i][j]);
            add(tot,n*(n-1)+i,inf);//明明是n*n的矩阵,为什么这里是n*(n-1)呢?当时xiaoping同志就跟我说了,D和GJ都决定了
                                   //,当i==j时是不会被建边的,因此每行只建了n-1个点
            add(tot,n*(n-1)+j,inf);
            tot++;
        }
    }
    for(int i=1;i<=n;i++){
        int num=s[i]-'0';
        add(tot,E,a[num]);
        add(tot,n*n+1+num,inf);//为什么这里要+1呢,因为序列中是有0的,而tot的上限是n*(n-1)+n所以只有+1才能保证不会建重复
        tot++;
    }
    for(int i=0;i<10;i++){
        add(tot,E,b[i]-a[i]);
        tot++;
    }

   /* for(int i=0;i<tot;i++){
        printf("%d %d %d\n",i,edge[i].to,edge[i].flow);
    }*/
}
int layer[maxn];
bool find_layer(){
    memset(layer,-1,sizeof(layer));
    layer[0]=0;
    queue<int>q;
    q.push(0);
    while(!q.empty()){
        int now=q.front();
        q.pop();
        for(int i=head[now];i!=-1;i=edge[i].next){
            int to=edge[i].to;
            if(layer[to]==-1 && edge[i].flow){
                layer[to]=layer[now]+1;
                q.push(to);
                if(to==E)return true;
            }
        }
    }
    return false;
}

int dfs(int pow,int flow){
    if(pow==E)return flow;
    int sum=0;
    for(int i=head[pow];i!=-1;i=edge[i].next){
        int to=edge[i].to;
        if(edge[i].flow && layer[to]==layer[pow]+1){
            int temp=dfs(to,min(flow-sum,edge[i].flow));
            edge[i].flow-=temp;
            edge[i^1].flow+=temp;
            sum+=temp;
            if(sum==flow)return sum;
        }

    }
    if(sum==0)layer[pow]=-1;
    return sum;
}
int dinic(){
    while(find_layer()){
        ans-=dfs(0,inf);
    }
}

int main(){
    int test;
    cin>>test;
    int Case=1;
    while(test--){
        init();
        build();
        dinic();
        printf("Case #%d: ",Case);
        cout<<ans<<endl;
        Case++;
    }
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值