| Section 2.3 | DONE | 2012.09.08 | PROB The Longest Prefix [ANALYSIS] |
| DONE | 2012.09.09 | PROB Cow Pedigrees [ANALYSIS] | |
| DONE | 2012.09.09 | PROB Zero Sum [ANALYSIS] | |
| DONE | 2012.09.09 | PROB Money Systems [ANALYSIS] | |
| DONE | 2012.09.12 | PROB Controlling Companies [ANALYSIS] |
2.3关开始出现更多动态规划问题。
The Longest Prefix
给定一个字符串集合S={s1,s2,s3...}和一个长字符串s,S中的字符串可以拼接出的s的最长前缀。
如果我们用dp[l]表示长为l的前缀能否被拼接出来(true表示能,false表示不能),则状态转移方程:dp[l]=ORk ( dp[l-length(sk)] and sk等于"s[l-length(sk)+1]s[l-length(sk)+2]...s[l]" ),其中OR表示“或”逻辑,and表示“与”逻辑。于是我们可以边输入边动归,试验集合中每个字符串。
点击查看我的代码:
/*
ID: xxxxxxx
LANG: C++
TASK: prefix
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char str[200001];
bool dp[200000];
char set[201][11];
int len[201];
int numset;
int tail;
FILE *fin,*fout;
int max;
int main(){
fin=fopen("prefix.in","r");
fout=fopen("prefix.out","w");
numset=0;
while(fscanf(fin,"%s",set[numset]) && set[numset][0]!='.'){
len[numset]=strlen(set[numset]);
numset++;
}
char c;
tail=-1;
max=0;
while((c=fgetc(fin))!=EOF){
if(c=='\n')continue;
str[++tail]=c;
dp[tail]=false;
int i;
for(i=0;i<numset;i++){
if(tail+1>=len[i]){
if(tail+1>len[i] && !dp[tail-len[i]])continue;
int j;
for(j=0;j<len[i];j++){
if(str[tail-(len[i]-j)+1]!=set[i][j])break;
}
if(j==len[i]){
dp[tail]=true;
if(tail+1>max)max=tail+1;
break;
}
}
}
}
fprintf(fout,"%d\n",max);
return 0;
}
Cow Pedigrees
一种二叉树每个节点都有零个或两个儿子节点,用N个节点构造一个高度为K的这种二叉树一共有多少种方法?这里的高度指的是根节点到叶子节点的最多节点数。
二叉树有天然的子结构。所以如果用dp[n][k]表示n个节点构造高度为k的二叉树的放法数,我们有这样一个状态转移方程:
/*
ID: xxxxxxx
LANG: C++
TASK: nocows
*/
#include<stdio.h>
#include<stdlib.h>
FILE *fin,*fout;
int N,K;
int dp[200][100];
int main(){
fin=fopen("nocows.in","r");
fout=fopen("nocows.out","w");
fscanf(fin,"%d %d",&N,&K);
if(N%2==0){
fprintf(fout,"%d\n",0);
return 0;
}
int n,k;
for(n=1;n<=N;n++)
dp[n][0]=0;
for(k=1;k<=K;k++)
dp[1][k]=1;
for(n=3;n<=N;n+=2)
for(k=1;k<=K;k++){
dp[n][k]=0;
int i;
for(i=1;i<=n-2;i++)
dp[n][k]+=(dp[i][k-1]*dp[n-1-i][k-1])%9901;
dp[n][k]%=9901;
}
int ans=dp[N][K]-dp[N][K-1];
if(ans<0)
ans+=9901;
fprintf(fout,"%d\n",ans);
return 0;
}
Zero Sum
在一列数1 2 3 ... N之间插入加号,减号或者空格,组合成一个算式,输出算式结果为0的那些
又是简单粗暴的枚举,dfs搜索所有情况,然后对算式求值:
/*
ID: xxxxxxx
LANG: C++
TASK: zerosum
*/
#include<stdio.h>
#include<stdlib.h>
FILE *fin,*fout;
int N;
int num[9]={1,2,3,4,5,6,7,8,9};
char sign[8];
void check(){
int i;
int sum=0;
int n=1;
int pre=1;
for(i=0;i<=N-2;i++){
if(sign[i]==' '){
n=n*10+num[i+1];
}
else if(sign[i]=='+'){
sum+=pre*n;
pre=1;
n=num[i+1];
}
else if(sign[i]=='-'){
sum+=pre*n;
pre=-1;
n=num[i+1];
}
}
sum+=pre*n;
if(sum==0){
fprintf(fout,"%d",num[0]);
for(i=1;i<N;i++){
fprintf(fout,"%c%d",sign[i-1],num[i]);
}
fprintf(fout,"\n");
}
}
void dfs(int c){
if(c==N-1){
check();
return;
}
sign[c]=' ';
dfs(c+1);
sign[c]='+';
dfs(c+1);
sign[c]='-';
dfs(c+1);
return;
}
int main(){
fin=fopen("zerosum.in","r");
fout=fopen("zerosum.out","w");
fscanf(fin,"%d",&N);
dfs(0);
return 0;
}
Money Systems
用几种面值的硬币组合出一个钱数一共有多少种办法?完全背包问题,在初始化边界条件和设计状态转移方程的时候有一定技巧,详情见经典的背包九讲。我们用dp[v][n]表示前v种硬币组合出钱数n的方法数,状态转移方程:
dp[v][n]=dp[v-1][n]+dp[v][n-value(v)]
/*
ID: xxxxxxx
LANG: C++
TASK: money
*/
#include<stdio.h>
#include<stdlib.h>
FILE *fin,*fout;
int V,N;
int value[26];
long long dp[26][10001];
int main(){
fin=fopen("money.in","r");
fout=fopen("money.out","w");
fscanf(fin,"%d %d",&V,&N);
int v,n;
for(v=1;v<=V;v++)
fscanf(fin,"%d",&value[v]);
for(v=0;v<=V;v++)
dp[v][0]=1;
for(n=1;n<=N;n++)
dp[0][n]=0;
for(v=1;v<=V;v++){
for(n=1;n<=N;n++){
dp[v][n]=dp[v-1][n];
if(n-value[v]>=0)
dp[v][n]+=dp[v][n-value[v]];
}
printf("\n");
}
fprintf(fout,"%lld\n",dp[V][N]);
return 0;
}
Controlling Companies
着实被这道题卡了不少时间,实际上不用考虑太多,一个二维表表示公司之间控股情况,不断借助可控的公司更新这个表,直到这个表不在变化,就表示我们得到了最终的结果。我认为这个题没有什么可以发掘的,想多了反而容易钻牛角尖。
/*
ID: xxxxxxx
LANG: C++
TASK: concom
*/
#include<stdio.h>
#include<stdlib.h>
FILE *fin,*fout;
int N;
int control[101][101];
int count[101][101];
bool done[101][101];
int main(){
fin=fopen("concom.in","r");
fout=fopen("concom.out","w");
fscanf(fin,"%d",&N);
int i,j,p;
while(N){
fscanf(fin,"%d %d %d",&i,&j,&p);
count[i][j]=control[i][j]=p;
N--;
}
for(i=1;i<=100;i++){
while(1){
bool change=false;
for(j=1;j<=100;j++){
if(count[i][j]>50&&!done[i][j]){
done[i][j]=true;
change=true;
for(p=1;p<=100;p++)
if(i!=p)
count[i][p]+=control[j][p];
}
}
if(!change)break;
}
}
for(i=1;i<=100;i++)
for(j=1;j<=100;j++)
if(i!=j&&count[i][j]>50)
fprintf(fout,"%d %d\n",i,j);
return 0;
}

2024年1月16日 10:02
I truly like you're composing style, incredible data, thankyou for posting