NO.1
A - HaHa's Morning
状压dp
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.
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.
3 2 1 3 2 3 2 2 1 2 2 1
2
0
题意:
即:一个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
The first line contains string A.
The second line contains string B.
The length of both strings will not be greater than 100.
zzzzzfzzzzz abcdefedcba abababababab cdcdcdcdcdcd
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;
}