副本dp

NO.1

 

A - HaHa's Morning

状压dp

HaHa is so happy today, he is going to participate the 7th Hunan University Programming Contest. He woke up in the morning, and wanted to reach Hunan University as soon as possible, but he realized that he still has N things to do before going on his journey.
At first, HaHa thought there must have N! (The factorial of N) ways to get everything done, however, he soon found that this was impossible at all, for the work has some annoying restrictions: some things must be done before getting some other things done. Now HaHa is interested in the number of ways to get everything done, and he asks you for help, so your task is to find how many ways are there to finish his work.
Input
There are several test cases, each case contains several lines, and the first line of each case is two natural numbers N (that described above) and M ≤ 400 (for the total restrictions for the work).
The next M lines describes the restrictions, for each line, there is two positive integers A, B, for the A-th thing must be done before the B-th thing.
The input will finish with the end of file, input is guaranteed that 1 ≤ A, B ≤ N ≤ 17.
Output
For each the case, output one number: the ways to finish the work.
Sample Input
3 2
1 3
2 3
2 2
1 2
2 1
Sample Output

2
0

题意:

这个题目就是问你把n个数按照他给的排列顺序排好,一共有多少种排法

即:一个N个点的有向图,有M条有向边,问你拓扑排序可行的方式有多少种。





题解  ooo

对于一个点i来讲,如果其所有儿子节点都排列成功了,那么这个点也就排列成功了。

那么设定dp【i】表示排列成功的状态为i所可行的方案数个数。

那么就有Dp【q】+=dp【v】,q=v+(1<<i);

其中需要满足v&son[i]==son[i]&&v&(1<<i)==0.(当前点i的儿子都排列完了,这个点才能进行排列);


#include <iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdio>

using namespace std;

const int maxn=1<<18;
long long  dp[maxn];
int son[20];
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        memset(son,0,sizeof(son));///别忘了
        for(int i=0;i<m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            x--; y--;
            son[x]|=(1<<y);///每一位数的后节点 储存在二进制的y位
        }
        memset(dp,0,sizeof(dp));
        dp[0]=1;
        for(int i=0;i<(1<<n);i++)
        {

            for(int j=0;j<n;j++)
            {
                if((i&son[j])==son[j])///如果这种方案里面包含j的所有儿子
                {
                    if((i&(1<<j))==0)///并且j本身不在i方案里面
                    {
                        dp[(i|(1<<j))]+=dp[i];///这种方案加到带j的后来的大方案里面
                    }
                }
            }
        }
        printf("%lld\n",dp[(1<<n)-1]);///输出最后答案

    }
    return 0;
}




B - String painter

hdu(2476)  区间dp

There are two strings A and B with equal length. Both strings are made up of lower case letters. Now you have a powerful string painter. With the help of the painter, you can change a segment of characters of a string to any other character you want. That is, after using the painter, the segment is made up of only one kind of character. Now your task is to change A to B using string painter. What’s the minimum number of operations?

Input
Input contains multiple cases. Each case consists of two lines:
The first line contains string A.
The second line contains string B.
The length of both strings will not be greater than 100.
Output
A single line contains one integer representing the answer.
Sample Input
zzzzzfzzzzz
abcdefedcba
abababababab
cdcdcdcdcdcd
Sample Output
6
7


还是看题解过的,等自己a出来的那天

题意是说:

给你两个字符串,A和B,每一次我们可以使用一种颜色(用字母代替)刷任意位子的连续子序列,问将字符串A变成字符串B最少需要刷多少次。


关于思路,参考博客here

1.先刷出一个b来,不考虑a,看刷出一个b最少需要多少笔(区间dp):

    状态转移是

     dp[i][j]  有四种刷法  :

       (1)从刷好的 i+1 - > j,然后从 i+1 往前刷到 i,如果 i+1 和 i 相同的话,直接刷,否则需要多刷一笔: dp[i+1][j]+oj(i,i+1);

       (2)从刷好的 i+1 - > j,然后从 j 往前刷到 i,如果 i 和 j 相同的话,直接刷,否则需要多刷一笔: dp[i+1][j]+oj(i,j);

       (3)从刷好的 i - > j-1,然后从 j-1 往前刷到 i,如果 j 和 j-1相同的话,直接刷,否则需要多刷一笔: dp[i][j-1]+oj(j,j-1);

       (4)从刷好的 i - > j-1,然后从 i 往后刷到 j,如果 i 和 j 相同的话,直接刷,否则需要多刷一笔: dp[i][j-1]+oj(i,j);

    然后从区间 i 到 j 里面选择一个,断开刷比较大小

2. 然后看a 串,对于每位a 它可以有dp【0】【i】种刷法,但是如果此位a与b相同,可以省去一次刷,所以进行比较,

如果a【i】==b【i】 那么当前f【i】可以试于前一个f【i-1】相同;

否则,单独刷成,更新寻找最小值;


代码::::::--------------------------------------------------》》》》》》》》》》》》

#include <iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>
#include<valarray>

using namespace std;

const int maxn=200+5;
const int inf=0x3f3f3f3f;
int dp[maxn][maxn];
int f[maxn];
string a,b;

int ju(int i,int j)
{
    if(b[i]==b[j])
        return 0;
    else
        return 1;
}
int main()
{
    while(cin>>a>>b)
    {
        int n=a.size();
        memset(dp,inf,sizeof(dp));
       for(int l=1;l<=n;l++)
       {
           for(int i=0;i+l<=n;i++)
           {
               int j=i+l-1;
               if(l==1)
               {
                   dp[i][j]=1;
               }
               if(i+1<n) dp[i][j]=min(dp[i][j],dp[i+1][j]+ju(i,i+1));
               if(i+1<n) dp[i][j]=min(dp[i][j],dp[i+1][j]+ju(i,j));
               if(j-1>=0) dp[i][j]=min(dp[i][j],dp[i][j-1]+ju(j,j-1));
               if(j-1>=0) dp[i][j]=min(dp[i][j],dp[i][j-1]+ju(i,j));
               for(int k=i;k<j;k++)
               {
                   dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
               }
           }
       }
       for(int i=0;i<n;i++)
       {
           f[i]=dp[0][i];
           if(a[i]==b[i]) f[i]=f[i-1];
           else
           {
               for(int j=0;j<i;j++)
                f[i]=min(f[i],f[j]+dp[j+1][i]);
           }
       }
       cout<<f[n-1]<<endl;

    }
    return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值