Chieh's Blog

Because Of Coding

ZOJ Problem Set - 2029 The Intervals

飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2029

题意:就是给你n个数,和m个查询,每个查询有个数q.从n个数选两个数,假设为a,b(a<b),使得q在区间[a,b)内。并且要求这个b-a最小。

分析:起先要是b-a最小又要包含t。我们可以将n个数排序,然后取两个数a,b,使得,a<=t<b,并且a和b越接近越好,为什么呢?因为越接近的话b-a就越小,因为我们排了序。然后具体a和b我们可以二分查找位置,然后特殊情况稍微处理一下就可以了,最后啪啪啪就可以AC了

/*
Author:Chieh
Because Of Coding
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#define LL long long
#define INF 1e9
#define EPS 1e-9
using namespace std;
vector<int> V;
int n,m;
void init()
{
    V.clear();
    int u;
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&u);
        V.push_back(u);
    }
    sort(V.begin(),V.end());
}

void play()
{
    int t;
    while(m--)
    {
        scanf("%d",&t);
        int pos1=lower_bound(V.begin(),V.end(),t)-V.begin();
        int pos2=upper_bound(V.begin(),V.end(),t)-V.begin();
        if(pos1==n||pos2==n)
        {
            printf("no such interval\n");
            continue;
        }
        if(pos1!=pos2)
        {
            printf("[%d,%d)\n",V[pos1],V[pos2]);
            continue;
        }
        if(pos1==0)
        {
            printf("no such interval\n");
            continue;
        }
        printf("[%d,%d)\n",V[pos1-1],V[pos1]);

    }
    printf("\n");
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        play();
    }
    // cout << "Hello world!" << endl;
    return 0;
}

ZOJ Problem Set - 3890 Wumpus

飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3890

题意:有个人站在(0,0)位置,然后给你n*n的地图,其中地图中有黄金,怪物,坑,平地,因为题目简化了,坑不能进,怪物的地方不能进,黄金只有1块。人物的动作可以是转向,向前,挖金子,从(0,0)点出来,其中每个动作要花费10点的值,黄金值1000点值。求出来之后最大的值是多少.

分析:一看n<=20,且方向就只有4个,那么可以建立vis[20][20][4]的数组进行BFS模拟。

(1)由于数据非常小,所以我考虑到一点事情,就是如果黄金在sx,sy上,然后我们到达sx,sy,方向为1的值和到达sx,sy方向为2的值相同,然后从1到(0,0)比从2到(0,0)慢,那么如果我们到达1的时候就结束BFS,那么答案就错了,所以我是枚举到达sx,sy四个方向的值,然后再从这四个方向继续BFS回到(0,0),然后求出最小值就好,其中一些细节不符合条件的直接输出-1就好。虽然代码有点长,但是也不是特边复杂

(2)可以直接打标记,就是假设黄金在sx,sy,如果到了sx,sy,那么直接标记拿到了黄金,最后到了(0,0)之后,如果拿到了黄金那么就是最优值,这样代码就边的比较短了

最后啪啪啪就可以AC了。

/*
Author:Chieh
Because Of Coding
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#define LL long long
#define INF 1e9
#define EPS 1e-9
using namespace std;
struct he
{
    int x,y,d,val;
};
const int maxn=2*12;
int ai[maxn][maxn];
bool vis[maxn][maxn][5];
int n,T;
int ex,ey;
void init()
{
    scanf("%d",&n);
    memset(ai,0,sizeof(ai));
    for(int i=0; i<=n; i++)ai[0][i]=ai[i][0]=ai[n+1][i]=ai[i][n+1]=2;
    int u,v,w;
    while(scanf("%d%d%d",&u,&v,&w)!=EOF&&u!=-1)
    {

        v++;
        w++;
        if(u==3)
        {
            ex=v;
            ey=w;
        }
        ai[v][w]=u;
    }
}
pair<int,int> calD(int d,int x,int y)
{
    if(d==1)return make_pair(x+1,y);
    if(d==2)return make_pair(x,y+1);
    if(d==3)return make_pair(x-1,y);
    if(d==4)return make_pair(x,y-1);
}
int BFS1(int dd)
{
    memset(vis,0,sizeof(vis));
    queue<he> Q;
    Q.push((he)
    {
        1,1,1,0
    });
    vis[1][1][1]=1;
    while(!Q.empty())
    {
        he now=Q.front();
        Q.pop();
        int nx=now.x;
        int ny=now.y;
        int nd=now.d;
        int nv=now.val;
        if(ai[nx][ny]==3&&nd==dd)
        {
            return nv;
        }
        pair<int,int> need=calD(nd,nx,ny);
        int x=need.first;
        int y=need.second;
        if(ai[x][y]!=1&&ai[x][y]!=2&&!vis[x][y][nd])
        {
            vis[x][y][nd]=1;
            Q.push((he)
            {
                x,y,nd,nv+10
            });
        }
        int d=nd+1;
        if(d==5)d=1;
        if(!vis[nx][ny][d])
        {
            vis[nx][ny][d]=1;
            Q.push((he)
            {
                nx,ny,d,nv+10
            });
        }
        d=nd-1;
        if(d==0)d=4;
        if(!vis[nx][ny][d])
        {
            vis[nx][ny][d]=1;
            Q.push((he)
            {
                nx,ny,d,nv+10
            });
        }
    }
    return -1;

}
int BFS2(int x1,int y1,int dd)
{
    memset(vis,0,sizeof(vis));
    vis[x1][y1][dd]=1;
    queue<he> Q;
    Q.push((he)
    {
        x1,y1,dd,0
    });
    while(!Q.empty())
    {
        he now=Q.front();
        Q.pop();
        int nx=now.x;
        int ny=now.y;
        int nd=now.d;
        int nv=now.val;
        if(nx==1&&ny==1)
        {
            return nv;
        }
        pair<int,int> need=calD(nd,nx,ny);
        int x=need.first;
        int y=need.second;
        if(ai[x][y]!=1&&ai[x][y]!=2&&!vis[x][y][nd])
        {
            vis[x][y][nd]=1;
            Q.push((he)
            {
                x,y,nd,nv+10
            });
        }
        int d=nd+1;
        if(d==5)d=1;
        if(!vis[nx][ny][d])
        {
            vis[nx][ny][d]=1;
            Q.push((he)
            {
                nx,ny,d,nv+10
            });
        }
        d=nd-1;
        if(d==0)d=4;
        if(!vis[nx][ny][d])
        {
            vis[nx][ny][d]=1;
            Q.push((he)
            {
                nx,ny,d,nv+10
            });
        }
    }
    return -1;

}
void play()
{
    if(ai[1][1]==1||ai[1][1]==2)
    {
        printf("-1\n");
        return;
    }
    int MAX=-1;
    for(int i=1; i<=4; i++)
    {
        int ng=BFS1(i);
        if(ng==-1)
        {
            printf("-1\n");
            return;
        }
        int nb=BFS2(ex,ey,i);
        MAX=max(MAX,1000-20-nb-ng);

    }
    printf("%d\n",MAX);
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        play();
    }
    //  cout << "Hello world!" << endl;
    return 0;
}

ZOJ Problem Set - 3888 Twelves Monkeys

飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3888

题意:给你n年,其中有m中关系,Xi Yi,代表从第Xi年可以回到Yi年,其中 Xi>=Yi,然后给你Q个查询,每个查询一个年份q,问你从第q年,至少有两种方案可以回到q年之前的年份有多少年。然后后面就是给你解释,第q年可以等到q年之后然后在回到q年之前。或则直接走,但是m种关系在一条路径中只能用一次。

分析:假设当前查询的是q年,那么我们要知道我们能回到q年之前至少有两条路径有多少年,我们可以从q~n之间的最小Yi和次小Yi。为什么,因为q可以等到q~n之间的一年然后回到最小Yi,或则q可以等到q~n之间的一年然后回到次小Yi,这样就有两条路径可以回到Yi到q之间的年份,且因为次小Yi是第二小的,所以剩下的都大于次小Yi,所以次小Yi是最优的。那么就可以从后往前推,每次更新次小Yi。然后查询的话,如果次小Yi大于等于q,那么就是0,因为回不去,如果小于q,那么就可以会q-次小Yi年,这样的话就Ok了,最后啪啪啪就可以AC了

/*
Author:Chieh
Because Of Coding
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#define LL long long
#define INF 1e9
#define EPS 1e-9
using namespace std;
const int maxn=5*12345;
struct he
{
    int x,y;
} Sky[maxn];
int n,m,q;
bool cmp(he a,he b)
{

    return a.x>b.x;
}
void init()
{
    for(int i=1; i<=m; i++)scanf("%d%d",&Sky[i].x,&Sky[i].y);
    sort(Sky+1,Sky+1+m,cmp);
}
int ai[maxn];
void play()
{
    memset(ai,0,sizeof(ai));
    int l1=-1,l2=-1;
    for(int i=1; i<=m; i++)
    {
        int now=Sky[i].x;
        int next=Sky[i].y;
        if(l1==-1)l1=next;
        else if(l2==-1)
        {
            if(l1>next)
            {
                l2=l1;
                l1=next;
            }
            else
            {
                l2=next;
            }
        }
        else
        {
            if(next<l1)
            {
                l2=l1;
                l1=next;
            }
            else if(next<l2)
            {
                l2=next;
            }
        }
        ai[now]=l2;
    }

    for(int i=n; i>=1; i--)
    {
        if(ai[i]==-1)continue;
        if(ai[i]==0)
        {
            ai[i]=ai[i+1];
        }
    }
    int t;
    while(q--)
    {
        scanf("%d",&t);
        if(ai[t]==-1||ai[t]==0)printf("0\n");
        else
        {
            if(ai[t]>=t)printf("0\n");
            else printf("%d\n",t-ai[t]);
        }
    }
}
int main()
{
    while(scanf("%d%d%d",&n,&m,&q)!=EOF)
    {
        init();
        play();
    }
//    cout << "Hello world!" << endl;
    return 0;
}

ZOJ Problem Set - 3885 The Exchange of Items

飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3885

题意:给你n对数据Ai,Bi,Ai代表没有当前值,Bi代表目标值,然后有m个交换,Xi,Yi,说名Xi可以变为Yi,或则Yi可以变为Xi,求最小的交换次数,使得n个数据到达目标值。

分析:一看就知道,假设p为Ai-Bi,如果p大于0,那么i这个点必须要交换掉p个数,如果p<0那么就说明i必须要得到p个数,这样的话,如果我们想知道有可行的方案使得在改变后Ai全都变为Bi,我们可以使用最大流,但是这里我们将p>o加到s1里去,p<0加到s2里去,比较s1和s2是否相等,因为不等的话就肯定跑不了。如果相同,具体怎么建图?p大于0,那么就从超级源点加一条流量为p的边到i,如果小于0,那么就从i加一条流量为p的边到超级汇点,然后根据m个可以交换的边,加m条从Xi到Yi的边,和Yi到Xi的边,因为是双向的,且容量为INF,因为没有限制。这样设最大流max_flow,如果max_flow和s1相等,那么就是可行的。关键题目让我们求最少的交换次数,具体的交换次数就是经过Xi和Yi这条边多少次。那么想到这,应该就知道了,可以用最小费用最大流,p大于0,那么就从超级源点加一条流量为p的边到i且花费为0,如果小于0,那么就从i加一条流量为p的边到超级汇点,花费也为0,具体就是m条边的花费,因为经过一次就要加一次交换次数,那么就是流量为INF,花费为1,这样跑一边,如果max_flow和s1相等,那么输出最小花费就是答案,最后啪啪啪就可以AC了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define forr(i,f_start,f_end) for(int i=f_start;i<f_end;++i)
#define mem(x,i) memset(x,i,sizeof(x))
const int maxn = 123;
const int maxm = 123 * 8;
typedef long long LL;
const int INF = 0xfffffff;
int input()
{
    int a;
    scanf("%d", &a);
    return a;
}
int Flow;
///-----------------最小费用流-----------------
//cost 代表一个流量要的费用 cap代表容量
//init 到N+2,如果超级汇点就是0,N+1
//else 就是s到t init到多少个点加1
struct Edge
{
    int to, next, cap, flow, cost;
} edge[maxm];
struct MCMF
{
    int head[maxn], tol;
    int pre[maxn], dis[maxn];
    bool vis[maxn];
    int N;
    void init(int n)
    {
        N = n;
        tol = 0;
        memset(head, -1, sizeof(head));
    }

    void addedge(int u, int v, int cap, int cost)
    {
        edge[tol].to = v;
        edge[tol].cap = cap;
        edge[tol].cost = cost;
        edge[tol].flow = 0;
        edge[tol].next = head[u];
        head[u] = tol++;
        edge[tol].to = u;
        edge[tol].cap = 0;
        edge[tol].cost = -cost;
        edge[tol].flow = 0;
        edge[tol].next = head[v];
        head[v] = tol++;
    }

    bool spfa(int s, int t)
    {
        queue<int>q;
        for (int i = 0; i<N; i++)
        {
            dis[i] = INF;
            vis[i] = false;
            pre[i] = -1;
        }
        dis[s] = 0;
        vis[s] = true;
        q.push(s);
        while (!q.empty())
        {
            int u = q.front();
            q.pop();
            vis[u] = false;
            for (int i = head[u]; i != -1; i = edge[i].next)
            {
                int v = edge[i].to;
                if (edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost)
                {
                    dis[v] = dis[u] + edge[i].cost;
                    pre[v] = i;
                    if (!vis[v])
                    {
                        vis[v] = true;
                        q.push(v);
                    }
                }
            }
        }
        if (pre[t] == -1) return false;
        else return true;
    }
    //返回的是最大流,cost存的是最小费用
    int minCostMaxflow(int s, int t, int &cost)
    {
        int flow = 0;
        cost = 0;
        while (spfa(s, t))
        {
            int Min = INF;
            for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to])
            {
                if (Min > edge[i].cap - edge[i].flow)
                    Min = edge[i].cap - edge[i].flow;
            }
            for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to])
            {
                edge[i].flow += Min;
                edge[i ^ 1].flow -= Min;
                cost += edge[i].cost*Min;
            }
            flow += Min;
        }
        return flow;
    }
} mcmf;
///-----------------------------------------
int N, M;
int main()
{
    while (scanf("%d%d", &N, &M)!=EOF)
    {
        mcmf.init(N+2);
        int cmp=0;
        int cmp1=0;
        for(int i=1; i<=N; i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            int p=a-b;
            if(p>0)
            {
                cmp+=p;
                mcmf.addedge(0,i,p,0);

            }
            else if(p<0)
            {
                cmp1-=p;
                mcmf.addedge(i,N+1,-p,0);
            }
        }
        int a,b;
        for(int i=1; i<=M; i++)
        {
            scanf("%d%d",&a,&b);
            mcmf.addedge(a,b,INF,1);
            mcmf.addedge(b,a,INF,1);
        }
        int cost;
        int Flow = mcmf.minCostMaxflow(0, N+1, cost);
        if(cmp1!=cmp||cmp!=Flow)printf("-1\n");
        else   printf("%d\n",cost);

    }
    return 0;
}

ZOJ Problem Set - 1967 Fiber Network

飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=967

题意:给你个有向图,每条有向边有字母标识,然后有查询,每个给你u,v,从u到v路径上出现了那些字母,并且字母必须在每条路径的边上。

分析:假设只有一个字母的话,那么就很简单,因为n不是特别大,如果u到v有字母,那么就标记u可以到v,接着直接flyod跑一边,接着查询,就可以根据flyod跑出来是否联通给出答案。那么因为这里是有26个字母,所以可以跑26次flyod,然后对于每次询问,对26个字母都进行一遍查找,最后输出就好。最后啪啪啪就可以AC了

/*
Author:Chieh
Because Of Coding
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#define LL long long
#define INF 1e9
#define EPS 1e-9
using namespace std;
int n;
const int maxn=234;
int ai[27][maxn][maxn];
char ch[27];
void init()
{
    memset(ai,0,sizeof(ai));
    int u,v;
    while(scanf("%d%d",&u,&v)!=EOF&&u)
    {
        scanf("%s",ch+1);
        int len=strlen(ch+1);
        for(int i=1; i<=len; i++)
        {
            ai[ch[i]-'a'+1][u][v]=1;
        }

    }
    for(int l=1; l<=26; l++)
    {
        for(int k=1; k<=n; k++)
        {
            for(int i=1; i<=n; i++)
            {
                for(int j=1; j<=n; j++)
                {
                    if(ai[l][i][k]&&ai[l][k][j])ai[l][i][j]=1;
                }
            }
        }
    }
}

void play()
{
    int u,v;
    while(scanf("%d%d",&u,&v)!=EOF&&u)
    {
        bool flag=1;
        for(int i=1; i<=26; i++)
        {
            if(ai[i][u][v])
            {
                printf("%c",'a'+i-1);
                flag=0;

            }
        }
        if(flag)printf("-");
        printf("\n");
    }
    printf("\n");
}
int main()
{

    while(scanf("%d",&n)!=EOF&&n)
    {
        init();
        play();

    }
    //   cout << "Hello world!" << endl;
    return 0;
}

ZOJ Problem Set - 1995 Spiderman

飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=995

题意:就是给你n个数,它们的和不超过1000,接着就是你可以选择加或则减,就是说初始值是0,然后n步,你可以选择加上第i个数,或则减掉第i个数,但是减掉的时候不能小于0。求最后的数字是0,且使的加到的最大值最小。求出这个路径。

分析:这个题,刚开始的想法是,先是想想加加减减看看最后能不能到达0,那么直接可以用dp,因为有加又有减,所以一维数组就很难维护了,这里我就用二维数组,dp[i][j]表示第i个数过后,能不能到达j这个高度,那么看最后能不能成为0,就是能不能到达dp[n][0]了这样可以轻易的求出能不能到达0,但是现在我们需要知道最大值最小,所以需要维护点东西,这里分2中情况(这里定义ai为第i个数的大小)

(1)假设i-1能到达j,那么i肯定能到达j+ai[i],然后我们需要更新到达j+ai[i]的最大值,假设dp[i-1][j]为i-1过后,到达j的最大值的最小值,那么如果j+ai[i]>dp[i-1][j]的话,那么就得更新最大值的最小值,因为我们必须到达j+ai[i],反之的话就是dp[i-1][j],设这个值为qn,这个值是从dp[i-1][j]推过来的,所以更新dp[i][j+ai[i]]=min(dp[i][j+ai[i]],qn),这样高度最小

(2)假设i-1能到达j,且j-ai[i]>=0,那么肯定能到达j-ai[i],接着我们就可以根据第一种情况dp,但是有个地方不同,这里比较的是j和dp[i-1][j],因为j-ai[i]<j;接着就是跟第一种情况一样,设这里的只为pn,则dp[i][j-ai[i]]=min(dp[i][j-ai[i]],pn),

根据这两种我们就知道最大值最小是什么样的情况,最后需要输出,只要用一个数组li来记录,具体怎么记录?第一种情况,如果dp[i][j+ai[i]]比原来的数要小,那么就是li[i][j+ai[i]]指向i-1的j,另一种情况就是li[i][j-ai[i]]只想i-1的j,最后反推就是答案了,然后啪啪啪就可以AC了

/*
Author:Chieh
Because Of Coding
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#define LL long long
#define INF 1e9
#define EPS 1e-9
using namespace std;
const int maxn=1234;
int ai[maxn];
int dp[maxn][maxn];
int li2[maxn][maxn];
int T;
int n;
void init()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)scanf("%d",&ai[i]);
}
vector<int> V;
void play()
{
    for(int i=0; i<=n; i++)
    {
        for(int j=0; j<=1000; j++)
        {
            dp[i][j]=INF;
        }
    }
    dp[0][0]=0;
    for(int i=1; i<=n; i++)
    {
        for(int j=0; j<=1000; j++)
        {
            if(dp[i-1][j]==INF)continue;
            int q=j+ai[i];
            int p=j-ai[i];
            int qn=max(dp[i-1][j],q);
            if(q<=1000&&qn<dp[i][q])
            {
                dp[i][q]=qn;
                li2[i][q]=j;
            }
            int pn=max(dp[i-1][j],j);
            if(p>=0&&pn<dp[i][p])
            {
                dp[i][p]=pn;
                li2[i][p]=j;
            }
        }
    }
    if(dp[n][0]!=INF)
    {
        V.clear();
        V.push_back(2);
        int st=n-1;
        int now=li2[n][0];
        while(st>=1){
            int next=li2[st][now];
            int ff=now-next;
            if(ff>0)V.push_back(1);
            else V.push_back(2);
            st--;
            now=next;
        }
        bool first=1;
        for(int i=V.size()-1;i>=0;i--){
            if(V[i]==1)printf("U");
            else printf("D");
        }
        printf("\n");
    }
    else
    {
        printf("IMPOSSIBLE\n");
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        play();
    }
//    cout << "Hello world!" << endl;
    return 0;
}

ZOJ Problem Set - 2158 Truck History

飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1158

题意:就是给你n个字母,每个字母由7个字符组成,且都是小写字母,而且不相等,每个字母有一个父亲,且到父亲的距离是跟父亲位置相同字母不同的位置数量,且根节点没有父亲,求距离和最小。

分析:一看,每个节点有个父亲,那么就是一棵树,且距离可以直接定义为父节点到自己点的边长距离,那么要求n-1条边的距离和最小,直接用最小生成树算法即可,我们只要预处理出每对的距离,然后用prim算法即可,因为是稠密图,prim跑得比较优秀,如果是稀疏图,那么直接就kruskal。总体复杂就是,距离n*n*7,prim算法为n*n,所以复杂的为O(n*)n,最后啪啪啪就可以AC了

/*
Author:Chieh
Because Of Coding
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#define LL long long
#define INF 1e9
#define EPS 1e-9
using namespace std;
const int maxn=2*1234;
char ch[maxn][10];
int n;
bool vis[maxn];
void init()
{
    for(int i=1; i<=n; i++)scanf("%s",ch[i]+1);
}
short dist[maxn][maxn];
short low[maxn];
void play()
{
    int ans=0;
    for(int i=1; i<=n; i++)
    {
        dist[i][i]=0;
        for(int j=i+1; j<=n; j++)
        {
            int sum=0;
            for(int k=1; k<=7; k++)
            {
                if(ch[i][k]!=ch[j][k])sum++;
            }
            dist[i][j]=dist[j][i]=sum;
        }

    }
    memset(vis,0,sizeof(vis));
    vis[1]=1;
    int pos=1;
    for(int i=2; i<=n; i++)
    {
        low[i]=dist[1][i];
    }

    for(int i=1; i<n; i++)
    {
        int MIN=INF;
        int idx=-1;
        for(int j=1; j<=n; j++)
        {
            if(!vis[j]&&MIN>low[j])
            {
                MIN=low[j];
                idx=j;
            }
        }
        ans+=MIN;
        vis[idx]=1;
        for(int j=1; j<=n; j++)
        {
            if(!vis[j]&&low[j]>dist[idx][j])
            {

                low[j]=dist[idx][j];
            }
        }
    }
    printf("The highest possible quality is 1/%d.\n",ans);
}
int main()
{
    while(scanf("%d",&n)&&n)
    {
        init();
        play();
    }
    //cout << "Hello world!" << endl;
    return 0;
}

ZOJ Problem Set - 3777 Problem Arrangement

飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5264

题意:给你n个题目,每个题目放在第i个位置有个值,有个程序可以随机生成1~n的排列,求生成的排列值大于m的期望次数

分析:这里的话需要推一下公式,需要用到极限的定理,因为假设n!种方案里,可行的方案概率为p,不可行为q(即1-p),则期望次数为

limn->无穷p*q^0*1+p*q^1*2+p*q^2*3....+p*q^(n-1)*n这个极限怎么求是关键,这里可以用错位相减法,即将式子乘以q,则为

qlimn->无穷                p*q^1*1+p*q^2*2....+p*q^(n-1)*(n-1)+p*q^(n)*n,然后两式一减,为(1-q)limn->无穷p+p*q^1+p*q^2...p*q^(n-1)-p*q^n*n,等比求和,即limn->无穷p*(1-q^n)/(1-q)-p*q^(n)*n,求极限后为可知这个极限为1/(1-q)即1/p,所以期望的次数是1/p,现在要求的是满足大于等于m的排列有多少个,因为n最大只有12,所以可以用状态压缩,最大也就只有4095,具体怎么搞?假设当前要确定的是第i个位置的数字,然后假设第j个题目放在第i个位置,那么我们可以去找i-1个位置,因为i-1个位置可以用二进制表示,所以可以很快找到然后把i-1个位置里面的值加上j在第i个的值,最后的话,大于等于m的方案数就是我们要求的复杂度O(n*n*2^n),最后啪啪啪就可以AC了

/*
Author:Chieh
Because Of Coding
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#define LL long long
#define INF 1e9
#define EPS 1e-9
using namespace std;
const int maxn=13;
int C[maxn];
int ai[maxn][maxn];
int bi[5000][567];
int n,m,T;
void init()
{
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)scanf("%d",&ai[i][j]);
}
int gcd(int a,int b)
{
    if(a<b)swap(a,b);
    if(a%b==0)return b;
    return gcd(b,a%b);
}
void play()
{
    memset(bi,0,sizeof(bi));
    bi[0][0]=1;
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            for(int k=1; k<C[n]; k++)
            {
                int t=k;
                bool flag=1;
                int q=0;
                for(int l=n-1; l>=0; l--)
                {
                    if(l+1==j&&t-C[l]<0)
                    {
                        flag=0;
                        break;
                    }
                    if(t-C[l]>=0)
                    {
                        t-=C[l];
                        q++;
                    }
                }
                if(q!=i)flag=0;
                if(flag)
                {
                    for(int l=0; l<=500; l++)
                    {
                        int now=l+ai[j][i];
                        if(now>500)now=500;
                        bi[k][now]+=bi[k-C[j-1]][l];
                    }
                }
            }.
        }
    }
    int fz=0;
    int fm=1;
    for(int i=m; i<=500; i++)fz+=bi[C[n]-1][i];
    for(int i=1; i<=n; i++)fm*=i;
    if(fz==0)printf("No solution\n");
    else
    {
        int p=gcd(fz,fm);
        printf("%d/%d\n",fm/p,fz/p);
    }
}
int main()
{
    C[0]=1;
    for(int i=1; i<=12; i++)C[i]=C[i-1]*2;
    scanf("%d",&T);
    while(T--)
    {
        init();
        play();
    }

    return 0;
}

 

ZOJ Problem Set - 3768 Continuous Login

飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5231

题意:给你个数n,求最少的数,假设为a1...ak, 1+2+3...+a1+1+2+3+..a2.....+ak=n

分析:这题乍一看没有任何想法,但是我们可以暴力搜下,发现前100000最多只有3个,那么我们可以直接猜测最多只有三个,那么就可以开始搞了,先求出从1+2+。。。k>=123456789最大的n是多少,加入只有一个,直接二分查找,如果找不到,那么就尝试2个的,两个的话可以用夹逼定理,假设左边为1,右边为k,如果a1+ak>n那么就是太大了,r--,如果是<n,那么就是太小了l++,等于的话就说明2个可以,如果2个不可以,那么就三个,第一个数可以枚举,后面两个数可以用夹逼寻找,最后啪啪啪就可以AC了

/*
Author:Chieh
Grow up happy
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <map>
#include <stack>
#define LL long long
using namespace std;
int ai[16000];
int T,n;
int st;
void init()
{
    scanf("%d",&n);
}
int l,r;
bool Brsearch(int p)
{
     l=1;
     r=st-1;
    while(l<=r)
    {
        int ok=ai[l]+ai[r];
        if(ok==p)return 1;
        if(ok<p)l++;
        else r--;
    }
    return 0;

}
void play()
{
    int pos=lower_bound(ai+1,ai+st,n)-ai;
    if(ai[pos]==n)
    {
        printf("%d\n",pos);
        return;
    }
    if(Brsearch(n)){
        printf("%d %d\n",l,r);
        return;
    }
    for(int i=1;i<=n;i++){
        if(n>=ai[i]&&Brsearch(n-ai[i])){
            printf("%d %d %d\n",i,l,r);
            return;
        }
    }
}
int main()
{
    st=1;
    LL now=0;
    while(now<=123456789)
    {
        now+=st;
        ai[st]=now;
        st++;

    }

    scanf("%d",&T);
    while(T--)
    {
        init();
        play();
    }
    return 0;
}

ZOJ Problem Set - 3791 An Easy Game

飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5299

题意:给你n,k,m和两个长度为n的只包含'0'和'1'的字符串,给你k步,每步要正好改变s1字符串m个位置,使得k步后s1和s2相等,问总共有多少中方法,取模输出。

分析:这种题一看,以为是按字符串DP,其实发现如果按字符串DP 那复杂度就太大了我们发现字符串就只有0,1,所以可以按状态来DP,具体DP 的话,假设当前为第i步,我们需要去m个位置,具体m个位置我们可以,不一样的位置取j个,那么一样的位置就是m-j个,然后从i-1进行迭代。如果i-1存在不同位置有x个,相同位置 为n-x个,如果x>=j&&n-x>=m-j,且这个方法是可以行的通的,i-1时的方法数为ans,那么我们就可以从x里面取j个,n-x取m-j个,这里的话是组合所以我们可以预处理出C[100][100],最后答案怎么算呢?仔细想想就会有思路,因为x里去了j个,所以剩下不一样的位置为x-j个,但是一样的位置里面取了,m-j个,最后剩下的不一样的位置 有x-j+m-j个同理,一样的位置有n-x-(m-j)+j个,假设为j为js,m-j为os,x为js1,n-x为os1,且更新后的为x-j+m-j为njs,n-x-(m-j)+j为nos,则状态方程为 ai[i][njs][nos]+=ai[i-1][js1][os1]*C[js1][js]*C[os1][os];记得取模,复杂度为n^3,最后啪啪啪就可以AC了

/*
Author:Chieh
Grow up happy
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <map>
#include <stack>
#define LL long long
using namespace std;
const int MOD=1000000009;
const int maxn=123;
LL ai[maxn][maxn][maxn];
LL C[maxn][maxn];
char ch1[maxn],ch2[maxn];
int n,k,m;
void init()
{
    int diff=0;
    scanf("%s%s",ch1+1,ch2+1);
    int len=strlen(ch1+1);
    for(int i=1; i<=len; i++)
    {
        if(ch1[i]!=ch2[i])diff++;
    }
    memset(ai,0,sizeof(ai));
    ai[0][diff][len-diff]=1;
}
void play()
{

    for(int i=1; i<=k; i++)
    {
        for(int j=0; j<=m; j++)
        {
            int js=j;
            int os=m-j;
            for(int l=js; l<=100; l++)
            {
                int js1=l;
                int os1=n-l;
                if(os1<0)break;
                if(js1>=js&&os1>=os&&ai[i-1][js1][os1]>0)
                {
                    int njs=js1-js;
                    njs+=os;
                    int nos=os1-os;
                    nos+=js;
                    ai[i][njs][nos]+=((ai[i-1][js1][os1]*C[js1][js]%MOD)*C[os1][os]%MOD);
                    ai[i][njs][nos]%=MOD;
                }

            }
        }
    }
    printf("%lld\n",ai[k][0][n]);
}
int main()
{
    C[0][0]=1;
    for(int i=0; i<=100; i++)
    {
        C[i][0]=C[i][i]=1;
        for(int j=1; j<i; j++)
        {
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
        }
    }

    while(scanf("%d%d%d",&n,&k,&m)!=EOF)
    {
        init();
        play();
    }
    //cout << "Hello world!" << endl;
    return 0;
}