dp(状态压缩、树形dp)poj3254、HRBUST 1473、HRBUST 1477、HRBUST 1475、ZOJ 3662

来源:互联网 发布:程序员不善于沟通 编辑:程序博客网 时间:2024/06/07 14:15

poj3254:

Corn Fields

Time Limit: 2000 MS Memory Limit: 65536 KB

64-bit integer IO format: %I64d , %I64u Java class name: Main

[Submit] [Status] [Discuss]

Description

Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can't be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.

Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.

Input

Line 1: Two space-separated integers: M and N 
Lines 2..M+1: Line i+1 describes row i of the pasture with N space-separated integers indicating whether a square is fertile (1 for fertile, 0 for infertile)

Output

Line 1: One integer: the number of ways that FJ can choose the squares modulo 100,000,000.

Sample Input

2 31 1 10 1 0

Sample Output

9

题意:有些在帝能够放牧,有些不行,放牧的地方不能相邻,文有多少种方案

思路:状态压缩,dp[i][j]表示,到第i行,状态为j的有多少种方法。可以手先预处理出每一行合法的状态,对于当前的行,只需要判断这个状态跟前一行的状态不冲突,就加上

#include<iostream>#include<cstdio>#include<string>#include<cstring>#include<vector>#include<cmath>#include<queue>#include<stack>#include<map>#include<set>#include<algorithm>using namespace std;const int maxn=5010;const int MOD=100000000;int N,M;int dp[15][maxn];int A[maxn];int cnt;int val[maxn];bool can(int x){    return (x&(x<<1))==0;}bool judge(int x,int cur){    return (x&(val[cur]))==0;}int main(){    while(scanf("%d%d",&M,&N)!=EOF)    {        cnt=0;        for(int i=0;i<(1<<N);i++)            if(can(i))A[++cnt]=i;        memset(dp,0,sizeof(dp));        memset(val,0,sizeof(val));        for(int i=1;i<=M;i++)        {            int x;            for(int j=1;j<=N;j++)            {                scanf("%d",&x);                //反着加,方便后面判断不合法的状态                if(!x)val[i]+=(1<<(N-j));            }        }        for(int i=1;i<=cnt;i++)            if(judge(A[i],1))dp[1][i]=1;        for(int i=2;i<=M;i++)        {            for(int j=1;j<=cnt;j++)            {                if(!judge(A[j],i))continue;                for(int k=1;k<=cnt;k++)                {                    if(!judge(A[k],i-1))continue;                    if(A[j]&A[k])continue;//跟前一行的状态不冲突,也就是不相邻                    dp[i][j]=(dp[i][j]+dp[i-1][k])%MOD;                }            }        }        int ans=0;        for(int i=1;i<=cnt;i++)            ans=(ans+dp[M][i])%MOD;        printf("%d\n",ans);    }    return 0;}

HRBUST 1475

G - 国王的宴会 HRBUST 1475

Time Limit: 1000 MS Memory Limit: 65536 KB

64-bit integer IO format: %lld , %llu Java class name: Main

[Submit] [Status]

Description

C国在成果破解J国破坏交通的阴谋之后,国王决定宴请各位大臣,合理制定宴请的人的名单的任务就交给了作为国王智囊团团长的你。

你知道国王喜欢热闹,所以你希望能邀请尽量多的人,但是做为直接上下级关系的两个人直接出现在宴会上的时候会显得很尴尬,所以不能同时请有上下级关系的两个人。

国王是宴会的主办方,也是这个王国的最高领袖,所以必须到场。

为了能为宴会准备的更好,你需要知道整个宴会最多可以邀请多少宾客。

Input

有多组测试数据。

每组数据的第一行两个整数n(n<=1000)m分别表示整个王国的官员的数量和上下级关系的数量。

接下来m行每行两个整数ab表示ba的直接下级。

处理到文件结束。

Output

输出一个整数表示包括国王在内的宴会能邀请的人数最多的数量。

Sample Input

5 4

5 3

3 4

4 1

4 2

Sample Output

3

树形dp,dp[u][0]表示以u为根,不选u的最佳答案,dp[u][1]则表示选的最佳答案

dp[u][0]+=max(dp[v][0],dp[v][1]);

dp[u][1]+=dp[v][0];

其中v是u的孩子节点

#include<iostream>#include<cstdio>#include<string>#include<cstring>#include<vector>#include<cmath>#include<queue>#include<stack>#include<map>#include<set>#include<algorithm>using namespace std;const int maxn=10010;int N,M;int in[maxn];int root;int dp[maxn][2];int head[maxn],tot;struct node{    int v,next;}edge[maxn];void add_edge(int u,int v){    edge[tot].v=v;    edge[tot].next=head[u];    head[u]=tot++;}void DP(int u){    dp[u][0]=0,dp[u][1]=1;    for(int i=head[u];i!=-1;i=edge[i].next)    {        int v=edge[i].v;        DP(v);        dp[u][0]+=max(dp[v][0],dp[v][1]);        dp[u][1]+=dp[v][0];    }}int main(){    while(scanf("%d%d",&N,&M)!=EOF)    {        memset(in,0,sizeof(in));        memset(head,-1,sizeof(head));        tot=0;        for(int i=1;i<=M;i++)        {            int u,v;            scanf("%d%d",&u,&v);            add_edge(u,v);            in[v]++;        }        for(int i=1;i<=N;i++)            if(!in[i]){root=i;break;}        DP(root);        printf("%d\n",dp[root][1]);    }    return 0;}


HRBUST 1477

F - 战争与守卫 HRBUST 1477

Time Limit: 1000 MS Memory Limit: 65536 KB

64-bit integer IO format: %lld , %llu Java class name: Main

[Submit] [Status]

Description

J国和C国爆发了战争,J国派出了很多间谍深入到C国内部,经常在C国的城市之间炸毁道路,阻拦C国的军队,C国为了抓到这些间谍,需要派人在城市进行监视,一个城市只要有一个人监视,那么这个城市的所有的道路也都会被监视到,为了能够尽量多的士兵去前线打仗,监视的士兵要尽可能的少,希望靠你编程求出至少需要多少人能实现对所有城市之间道路的监视。

Input

有多组测试数据

每组测试数据的第一行,一个整数n表示C国城市的数量(编号为0-----n-1) n<=2000

接下来n行:i:(M)表示有M个城市和城市i之间有道路连通,然后是M个整数表示和i连通的城市的编号。

处理到文件结束,输入数据保证是一棵树。

Output

输出一个整数,表示至少需要多少士兵能实现对所有道路的监视。

Sample Input

4

0:(1) 1

1:(2) 2 3

2:(0)

3:(0)

Sample Output

1

跟上一个题差不多,分两种情况,也就是这个节点放或者不方,取最大值

#include<iostream>#include<cstdio>#include<string>#include<cstring>#include<vector>#include<cmath>#include<queue>#include<stack>#include<map>#include<set>#include<algorithm>using namespace std;const int maxn=2010;int N,M;vector<int> g[maxn];int vis[maxn][2];int dp[maxn][2];int DP(int i,int j,int f){    if(vis[i][j])return dp[i][j];    vis[i][j]=1;    int &ans=dp[i][j];    ans=1;    for(int k=0;k<g[i].size();k++)    {        int v=g[i][k];        if(v!=f)ans+=DP(v,1,i);    }    if(j||f<0)//如果这个点的父节点放了或者这个点是根节点,可以选择不方    {        int sum=0;        for(int k=0;k<g[i].size();k++)        {            int v=g[i][k];            if(v!=f)sum+=DP(v,0,i);        }        ans=min(ans,sum);    }    return ans;}int main(){    while(scanf("%d",&N)!=EOF)    {        int u,v,num;        for(int i=0;i<=N;i++)g[i].clear();        for(int i=1;i<=N;i++)        {            scanf("%d:(%d)",&u,&num);            for(int j=0;j<num;j++)            {                scanf("%d",&v);                g[u].push_back(v);                g[v].push_back(u);            }        }        int ans=0;        memset(vis,0,sizeof(vis));        for(int i=0;i<N;i++)            if(!vis[i][0])                ans+=DP(i,0,-1);        printf("%d\n",ans);    }    return 0;}

HRBUST 1473

E - 教主的遗产 HRBUST 1473

Time Limit: 1000 MS Memory Limit: 65536 KB

64-bit integer IO format: %lld , %llu Java class name: Main

[Submit] [Status]

Description

恭送教主!

教主在2012年7月19日上午10:48,坐上前往北京的火车,从此开始了高富帅的生活。

在教主的的大学四年ACM生涯中,他用事实告诉我们,要想在比赛拿奖,除了平时的刻苦努力外,很大一部分还要依赖比赛时是
做题策略,简单来讲就是做题顺序,唯有想把能过的都过掉,然后再过难题,这样才能在比赛中拿奖。
我们将用这个做题顺序量化表示,即AC系数,AC系数越大,拿奖可能性就越大。
在比赛中会给出n个题,不同做题顺序会有不同的AC系数,假如A先做,B后做的话,B对A的AC系数为4, 反过来 B先做,A后做的话,A对B的AC系数为5,说明先做B后做A将得到更高的AC系数。

设Sij表示第i题在第j题做完后才做获得的AC系数,有三道题a, b, c,做题顺序为bac,则系数和为:Sum = Sab + Scb + Sca。

求一个做题顺序,使得AC系数和最大。

Input

有多组测试数据。
对于每组测试数据,第一行为n(1<=n<=16),表示题目数量。
接下来有n行,每行n个数字,对于1+i行的第j个数字Sij(0 <= Sij <= 32), 表示第i题在第j题之后做的AC系数。

Output

每组测试数据输出一行,包含一个数,为最大系数和。

Sample Input

1

2
0 2 
4 0 
3
0 2 4 
4 0 25 
17 15 0 

Sample Output

0
4
46

dp[i][j]表示到达状态j,最后一个做的题是i的最大值

#include<iostream>#include<cstdio>#include<string>#include<cstring>#include<vector>#include<cmath>#include<queue>#include<stack>#include<map>#include<set>#include<algorithm>using namespace std;int a[20][20],dp[(1<<16)],N;int main(){    while(scanf("%d",&N)!=EOF)    {        memset(dp,-1,sizeof(dp));        for(int i=0;i<N;i++)            for(int j=0;j<N;j++)                scanf("%d",&a[i][j]);        for(int i=0;i<N;i++)            dp[(1<<i)]=0;        for(int st=1;st<(1<<N)-1;st++)        {            for(int i=0;i<N;i++)            {                if(st&(1<<i)) continue;                int tmp=0;                for(int j=0;j<N;j++)                    if(st&(1<<j))                        tmp+=a[i][j];                dp[(st|(1<<i))]=max(dp[(st|(1<<i))],dp[st]+tmp);            }        }        printf("%d\n",dp[(1<<N)-1]);    }    return 0;}

ZOJ3662

H - Math Magic ZOJ 3662

Time Limit: 3000 MS Memory Limit: 32768 KB

64-bit integer IO format: %lld , %llu Java class name: Main

[Submit] [Status]

Description

Yesterday, my teacher taught us about math: +, -, *, /, GCD, LCM... As you know, LCM (Least common multiple) of two positive numbers can be solved easily because of a * b = GCD (a, b) * LCM (a, b).

In class, I raised a new idea: "how to calculate the LCM of K numbers". It's also an easy problem indeed, which only cost me 1 minute to solve it. I raised my hand and told teacher about my outstanding algorithm. Teacher just smiled and smiled...

After class, my teacher gave me a new problem and he wanted me solve it in 1 minute, too. If we know three parameters N, M, K, and two equations:

1. SUM (A1, A2, ..., Ai, Ai+1,..., AK) = N 
2. LCM (A1, A2, ..., Ai, Ai+1,..., AK) = M

Can you calculate how many kinds of solutions are there for Ai (Ai are all positive numbers). I began to roll cold sweat but teacher just smiled and smiled.

Can you solve this problem in 1 minute?

Input

There are multiple test cases.

Each test case contains three integers N, M, K. (1 ≤ N, M ≤ 1,000, 1 ≤ K ≤ 100)

Output

For each test case, output an integer indicating the number of solution modulo 1,000,000,007(1e9 + 7).

You can get more details in the sample and hint below.

Sample Input

4 2 23 2 2

Sample Output

12

Hint

The first test case: the only solution is (2, 2).

The second test case: the solution are (1, 2) and (2, 1).

dp[now][i][j]表示当前状态,和为i,LCM为j的方法数,递推k次可得答案

#include<iostream>#include<cstdio>#include<string>#include<cstring>#include<vector>#include<cmath>#include<queue>#include<stack>#include<map>#include<set>#include<algorithm>using namespace std;const int maxn=1010;const int MOD=1e9+7;int N,M,K,cnt;int LCM[maxn][maxn];int num[maxn],pos[maxn];int dp[2][maxn][maxn/10];int main(){freopen("in.txt","r",stdin);    for(int i=1;i<=1000;i++)        for(int j=1;j<=1000;j++)            LCM[i][j]=i/__gcd(i,j)*j;    while(scanf("%d%d%d",&N,&M,&K)!=EOF)    {        cnt=0;        memset(pos,-1,sizeof(pos));        for(int i=1;i<=M;i++)            if(M%i==0)num[cnt]=i,pos[i]=cnt++;        memset(dp[0],-1,sizeof(dp[0]));        dp[0][0][0]=1;        for(int i=1;i<=K;i++)        {            memset(dp[i&1],-1,sizeof(dp[i&1]));            for(int j=i-1;j<=N;j++)            {                for(int k=0;k<cnt;k++)                {                    if(dp[(i+1)&1][j][k]==-1)continue;                    for(int r=0;r<cnt&&j+num[r]<=N;r++)                    {                        int l=j+num[r];                        int s=LCM[num[r]][num[k]];                        if(s<=M&&pos[s]!=-1)                        {                            s=pos[s];                            if(dp[i&1][l][s]==-1)dp[i&1][l][s]=0;                            dp[i&1][l][s]+=dp[(i+1)&1][j][k];                            if(dp[i&1][l][s]>=MOD)dp[i&1][l][s]-=MOD;                        }                    }                }            }        }        printf("%d\n",dp[K&1][N][pos[M]]==-1?0:dp[K&1][N][pos[M]]);    }    return 0;}




0 0
原创粉丝点击