在大型工程的施工前,我们把整个工程划分为若干个子工程,并把这些子工程编号为1、2、……、N;这样划分之后,子工程之间就会有一些依赖关系,即一些子工程必须在某些子工程完成之后才能施工。由于子工程之间有相互依赖关系,因此有两个任务需要我们去完成:首先,我们需要计算整个工程最少的完成时间;同时,由于一些不可预测的客观因素会使某些子工程延期,因此我们必须知道哪些子工程的延期会影响整个工程的延期,我们把有这种特征的子工程称为关键子工程,因此第二个任务就是找出所有的关键子工程,以便集中精力管理好这些子工程,尽量避免这些子工程延期,达到用最快的速度完成整个工程。为了便于编程,现在我们假设:
(1)根据预算,每一个子工程都有一个完成时间。
(2)子工程之间的依赖关系是:部分子工程必须在一些子工程完成之后才开工。
(3)只要满足子工程间的依赖关系,在任何时刻可以有任何多个子工程同时在施工,也既同时施工的子工程个数不受限制。
(4)整个工程的完成是指:所有子工程的完成。
例如,有五个子工程的工程规划表:
序号 | 完成时间 | 子工程1 | 子工程2 | 子工程3 | 子工程4 | 子工程5 |
---|---|---|---|---|---|---|
子工程1 | 5 | 0 | 0 | 0 | 0 | |
子工程2 | 4 | 0 | 0 | 0 | 0 | |
子工程3 | 12 | 0 | 0 | 0 | 0 | |
子工程4 | 7 | 1 | 1 | 0 | 0 | |
子工程5 | 2 | 1 | 1 | 1 | 1 |
其中,表格中第I+1行J+2列的值如为0表示“子工程I”可以在“子工程J”没完成前施工,为1表示“子工程I”必须在“子工程J”完成后才能施工。上述工程最快完成时间为14天,其中子工程1、3、4、5为关键子工程。
又例如,有五个子工程的工程规划表:
序号 | 完成时间 | 子工程1 | 子工程2 | 子工程3 | 子工程4 | 子工程5 |
---|---|---|---|---|---|---|
子工程1 | 5 | 0 | 1 | 0 | 0 | |
子工程2 | 4 | 0 | 0 | 0 | 0 | |
子工程3 | 12 | 0 | 0 | 1 | 0 | |
子工程4 | 7 | 1 | 1 | 0 | 0 | |
子工程5 | 2 | 1 | 1 | 1 | 1 |
上述的子工程划分不合理,因为无法安排子工程1,3,4的施工。
输入数据:
第1行为N,N是子工程的总个数,N≤200。
第2行为N个正整数,分别代表子工程1、2、……、N的完成时间。
第3行到N+2行,每行有N-1个0或1。其中的第I+2行的这些0,1,分别表示“子工程I”与子工程1、2、…、I-1、I+1、…N的依赖关系,(I=1、2、……、N)。每行数据之间均用一个空格分开。
输出数据:
如子工程划分不合理,则输出-1;
如子工程划分合理,则用两行输出:第1行为整个工程最少的完成时间。第2行为按由小到大顺序输出所有关键子工程的编号。
样例:
输入文件名:project. in
5
5 4 12 7 2
0 0 0 0
0 0 0 0
0 0 0 0
1 1 0 0
1 1 1 1
输出文件名:project. out
14
1 3 4 5
由于题目中的工程有依赖关系,我们可以用拓扑排序来划分阶段。
根据题目给出来的图来构造拓扑序列储存在数组a中,如果存在环则输出“-1”.
然后使用动态规划来求出每个工程的最早完成时间,状态转移方程为: eet[a[i]]=max{eet[j]}+time[a[i]];map[a[i],j]=1 (1<=i<=n,1<=j<=n)
最短时间就是max(eet[a[i]]);
至于关键工程,有一个简单的道理:
最后完成的工程就是关键工程,影响关键工程的工程也是关键工程。
由于第一题还没弄明白所以从网上找了个题解粘过来↑
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<set>
#define R0(i,n) for(int i=0;i<n;++i)
#define R1(i,n) for(int i=1;i<=n;++i)
#define cl(x,c) memset(x,c,sizeof x)
#define maxn 202
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
template <class T> inline void read(T&x){
bool f=false;char ch;
for (ch=getchar();ch<=32;ch=getchar());
if(ch==‘-‘)f=true,ch=getchar();
for(x=0;ch>32;ch=getchar()) x=x*10+ch-‘0‘;
if(f) x=-x;
}
template <class T> inline void write(T x){
if(x<0) putchar(‘-‘),x=-x;
if(x<10) putchar(x+‘0‘);
else write(x/10),putchar(x%10+‘0‘);
}
template <class T> inline void writeln(T x){
write(x),puts("");
}
int map[maxn][maxn],time[maxn],eet[maxn],into[maxn],a[maxn];
bool v[maxn];
int n,ans;
int main(){
freopen("project.in", "r", stdin);
freopen("project.out", "w", stdout);
read(n);
R1(i,n) read(time[i]);
cl(into,0);
R1(i,n)R1(j,n)if (i!=j)read(map[i][j]);
R1(i,n)R1(j,n)if(map[i][j]==1) ++into[i];
R1(i,n){
int j=1;
while(j <= n &&into[j] !=0)++j;
if(j>n){writeln(-1); return 0;}
a[i]=j;
into[j]=-1;
R1(k,n)if(map[k][j]==1)--into[k];
}
R1(i,n){
R1(j,n)
if(map[a[i]][j]==1)
if(eet[j]>eet[a[i]])eet[a[i]]=eet[j];
eet[a[i]]+=time[a[i]];
ans=max(ans,eet[a[i]]);
}
cl(v,0);
for(int i=n;i>=1;--i) {
if(eet[a[i]]==ans)v[a[i]]=1;
R1(j,n) if(map[j][a[i]]==1&&v[j]&&eet[a[i]]+time[j]==eet[j]) v[a[i]]=1;
}
writeln(ans);
R1(i,n)if(v[i])write(i),printf(" ");
}
某总公司拥有高效生产设备M台,准备分给下属的N个分公司。各分公司若获得这些设备,可以为总公司提供一定的盈利。
问:如何分配这M台设备才能使国家得到的盈利最大?求出最大盈利值。
分配原则:每个公司有权获得任意数目的设备,但总台数不得超过总设备数M。其中M<=100,N<=100。
输入数据:
第一行为两个整数M,N。接下来是一个N×M的矩阵,其中矩阵的第i行的第j列的数Aij表明第i个公司分配j台机器的盈利。所有数据之间用一个空格分隔。
输出数据:
只有一个数据,为总公司分配这M台设备所获得的最大盈利。
样例
输入文件名:machine.in
3 2
1 2 3
2 3 4
输出文件名:machine.out
4
设f[ i ][ j ]表示前i个公司分配j台机器的最大利润,则:
f[ i ][ j ]=max{f[ i-1 ][ k ]+_[ i ][ j-k ]} (1<=i<=n,1<=j<=m,0<=k<=j)
其中_[i,j]表示第i个公司分配j台机器的利润。
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdio>
#define R0(i,n) for(register int i=0;i<n;++i)
#define R1(i,n) for(register int i=1;i<=n;++i)
#define cl(x,c) memset(x,c,sizeof x)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
template <class T> inline void read(T&x){
bool f = false; char ch;
for (ch = getchar(); ch <= 32; ch = getchar());
if (ch == ‘-‘) f = true, ch = getchar();
for (x = 0; ch > 32; ch = getchar()) x = x * 10 + ch - ‘0‘;
if (f) x = -x;
}
template <class T> inline void write(T x){
if (x < 0) putchar(‘-‘), x = -x;
if (x < 10)
putchar(x + ‘0‘);
else
write(x / 10), putchar(x % 10 + ‘0‘);
}
template <class T> inline void writeln(T x){
write(x),puts("");
}
int m,n,_[110][110],f[110][110];
int main(){
freopen("machine.in","r",stdin);
freopen("machine.out","w",stdout);
read(m),read(n);
R1(i,n)R1(j,m)read(_[i][j]);
R1(i,n)R1(j,m)R0(k,j+1)f[i][j]=max(f[i][j],f[i-1][k]+_[i][j-k]);
writeln(f[n][m]);
return 0;
}
字符串是数据结构和计算机语言里很重要的数据类型,在计算机语言中,对于字符串我们有很多的操作定义,因此我们可以对字符串进行很多复杂的运算和操作。实际上,所有复杂的字符串操作都是由字符串的基本操作组成。
例如,把子串a替换为子串b,就是用查找、删除和插入这三个基本操作实现的。
因此,在复杂字符串操作的编程中,为了提高程序中字符操作的速度,我们就应该用最少的基本操作完成复杂操作。
在这里,假设字符串的基本操作仅为:删除一个字符、插入一个字符和将一个字符修改成另一个字符这三种操作。
我们把进行了一次上述三种操作的任意一种操作称为进行了一步字符基本操作。
下面我们定义两个字符串的编辑距离:对于两个字符串a和b,通过上述的基本操作,我们可以把a变成b或b变成a;那么,把字符串a变成字符串b需要的最少基本字符操作步数称为字符串a和字符串b的编辑距离。
例如,如a=“ABC”,b=“CBCD”,则a与b的编辑距离为2。
你的任务就是:编一个最快的程序来计算任意两个字符串的编辑距离。
输入数据:
第1行为字符串a;第2行为字符串b。注:字符串的长度不大于1000,字符串中的字符全为大写字母。
输出数据:
编辑距离。
样例
输入文件名:edit.in
ABC
CBCD
输入文件名:edit.out
2
这个一看就知道是DP,,但懒惰的毛病又犯当时也没怎么深入思考
设 _ [ i ][ j ]为使a的前i个变成和b的前j个相同的编辑距离
别问我下划线是什么鬼不改 _ [ i ][ j ]=_ [ i-1 ][ j-1 ] ( a[ i-1 ]==b[ j-1 ] )
改的话就是喜闻乐见的3选1
插入 _ [ i ][ j-1 ]+1
删除 _ [ i-1 ][ j ]+1
修改 _ [ i-1 ][ j-1 ]+1
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdio>
#define R0(i,n) for(register int i=0;i<n;++i)
#define R1(i,n) for(register int i=1;i<=n;++i)
#define cl(x,c) memset(x,c,sizeof x)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
template <class T> inline void read(T&x){
bool f = false; char ch;
for (ch = getchar(); ch <= 32; ch = getchar());
if (ch == ‘-‘) f = true, ch = getchar();
for (x = 0; ch > 32; ch = getchar()) x = x * 10 + ch - ‘0‘;
if (f) x = -x;
}
template <class T> inline void write(T x){
if (x < 0) putchar(‘-‘), x = -x;
if (x < 10)
putchar(x + ‘0‘);
else
write(x / 10), putchar(x % 10 + ‘0‘);
}
template <class T> inline void writeln(T x){
write(x),puts("");
}
template <class T> inline T mmn(T x,T y,T z){
return min(x,min(y,z));
}
int _[1003][1003];
char a[1003],b[1003];
int main(){
scanf("%s%s",&a,&b);
int m=strlen(a),n=strlen(b);
R1(i,m)_[i][0]=i;
R1(i,n)_[0][i]=i;
R1(i,m)R1(j,n)
if(a[i-1]==b[j-1])_[i][j]= _[i-1][j-1];
else _[i][j]= mmn(_[i-1][j],_[i][j-1],_[i-1][j-1]) +1;
writeln(_[m][n]);
return 0;
}
在现实生活中,我们经常遇到硬币找零的问题,例如,在发工资时,财务人员就需要计算最少的找零硬币数,以便他们能从银行拿回最少的硬币数,并保证能用这些硬币发工资。
我们应该注意到,人民币的硬币系统是100,50,20,10,5,2,1,0.5,0.2,0.1,0.05,0.02,0.01元,采用这些硬币我们可以对任何一个工资数用贪心算法求出其最少硬币数。
但不幸的是:我们可能没有这样一种好的硬币系统,因此用贪心算法不能求出最少的硬币数,甚至有些金钱总数还不能用这些硬币找零。
例如,如果硬币系统是40,30,25元,那么37元就不能用这些硬币找零;
95元的最少找零硬币数是3。
又如,硬币系统是10,7,5,1元,那么12元用贪心法得到的硬币数为3,而最少硬币数是2。
你的任务就是:对于任意的硬币系统和一个金钱数,请你编程求出最少的找零硬币数;
如果不能用这些硬币找零,请给出一种找零方法,使剩下的钱最少。
输入数据:
第1行,为N和T,其中1≤N≤500为硬币系统中不同硬币数;1≤T≤10000为需要用硬币找零的总数。
第2行为N个数值不大于65535的正整数,它们是硬币系统中各硬币的面值。
输出数据:
如T能被硬币系统中的硬币找零,请输出最少的找零硬币数。
如T不能被硬币系统中的硬币找零,请输出剩下钱数最少的找零方案中的所需的最少硬币数。
样例
输入文件名:coin.in
4 12
10 7 5 1
输出文件名:coin.out
2
完全背包
f [ j ]表示找i的钱最小数量
f [ j ]=min( f[ j ] , f[ j-a[i] ]+1)
算完从后往前找f[ j ] != INF的就是答案
↑找零剩下钱数最少自然就是找的钱最多了
一开始把完全背包转成01背包做的,又T又W
此题待进一步研究
真的么= =
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<set>
#define R0(i,n) for(int i=0;i<n;++i)
#define R1(i,n) for(int i=1;i<=n;++i)
#define cl(x,c) memset(x,c,sizeof x)
#define maxn 50
#define maxt 100000
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
template <class T> inline void read(T&x){
bool f=false;char ch;
for (ch=getchar();ch<=32;ch=getchar());
if(ch==‘-‘)f=true,ch=getchar();
for(x=0;ch>32;ch=getchar()) x=x*10+ch-‘0‘;
if(f) x=-x;
}
template <class T> inline void write(T x){
if(x<0) putchar(‘-‘),x=-x;
if(x<10) putchar(x+‘0‘);
else write(x/10),putchar(x%10+‘0‘);
}
template <class T> inline void writeln(T x){
write(x),puts("");
}
int f[maxt],a[maxn],n,t;
int main(){
read(n),read(t);
R1(i,n)read(a[i]);
cl(f,0x3f);
f[0]=0;
R1(i,n)for(int j=a[i];j<=t;++j)f[j]=min(f[j],f[j-a[i]]+1);
if(f[t]!=INF) writeln(f[t]);
else for(int i=t;i>=0;--i) if(f[t]!=INF) writeln(f[t]);
return 0;
}
DP是硬伤,一直处于一种找不到感觉的状态。
之前就没怎么学扎实,现在往后学又导致了遗忘,就像“黑瞎子掰棒子”一样。
这样下去只会学的越多会的越少。
准备来一波复习吧。
仰天大笑出门去,无人知是荔枝来
また あした~
原文地址:http://blog.csdn.net/iostream0/article/details/43977155