专题:DP杂题1

A POJ 1018 Communication System
B POJ 1050 To the Max
C POJ 1083 Moving Tables
D POJ 1125 Stockbroker Grapevine
E POJ 1143 Number Game
F POJ 1157 LITTLE SHOP OF FLOWERS
G POJ 1163 The Triangle
H POJ 1178 Camelot
I POJ 1179 Polygon
J POJ 1189 钉子和小球
K POJ 1191 棋盘分割
L POJ 1208 The Blocks Problem
M POJ 1276 Cash Machine
N POJ 1322 Chocolate
O POJ 1414 Life Line
P POJ 1456 Supermarket
Q POJ 1458 Common Subsequence
R POJ 1609 Tiling Up Blocks
S POJ 1644 To Bet or Not To Bet
T POJ 1664 放苹果
U POJ 1690 (Your)((Term)((Project)))
V POJ 1699 Best Sequence
W POJ 1740 A New Stone Game
X POJ 1742 Coins
Y POJ 1887 Testing the CATCHER
Z POJ 1926 Pollution

点击题号进入题面

---

A

/*
dp[i,j]
i个设备
j最小带宽
只能转移到dp[i-1][j]+大于等于j带宽的设备
*/
#include <stdio.h>
#include <math.h>
#include <iostream>
#include <string.h>
using namespace std;
const int maxn=1e3+10;
const int maxm=1e6+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
int dp[maxn][maxn];
int num[maxn];
int p[maxn][maxn],f[maxn][maxn];
int main(){
//#define test
#ifdef test
	freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif

	cin>>casn;
	while(casn--){
		int mx=0;
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>num[i];
			for(int j=1;j<=num[i];j++){
				cin>>f[i][j]>>p[i][j];
				mx=max(f[i][j],mx);
			}
		}
		memset(dp,0,sizeof dp);
		for(int i=1;i<=n;i++){
			for(int j=0;j<=mx;j++){
				int mn=INF;
				for(int k=1;k<=num[i];k++){
					if(j<=f[i][k]){
						mn=min(dp[i-1][j]+p[i][k],mn);
					}
				}
				dp[i][j]=mn;
			}
		}
		double ans=0;
		for(int i=1;i<=mx;i++){
			ans=max(ans,i/(double)dp[n][i]);
		}
		printf("%.3f
",ans);
	}
#ifdef test
	fclose(stdin);fclose(stdout);system("out.txt");
#endif
	return 0;
}

B

/*
把二维看成一维
先枚举行的起点和终点
再把起点行和终点行间每一列的数值压缩到每一个点上
然后求一个最长连续字段和
复杂度O(n^3)
*/
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1e3+10;
const int maxm=1e6+10;
const int INF=0x3f3f3f3f;
#define ll long long
int casn,n,m,k;
int smax(int a[],int len){
	int mx=0,sub=0;
	for(int i=1;i<=len;i++){
		sub=max(a[i],sub+a[i]);
		mx=max(sub,mx);
	}
	return mx;
}
int arr[maxn];
int dp[maxn][maxn];
int main(){
#define test
#ifdef test
	freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif

	while(~scanf("%d",&n)){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				scanf("%d",&dp[i][j]);
			}
		}
		int ans=-INF;
		for(int i=1;i<=n;i++){
			memset(arr,0,sizeof arr);
			for(int j=i;j<=n;j++){
				for(int k=1;k<=n;k++){
					arr[k]+=dp[j][k];
				}
				ans=max(ans,smax(arr,n));
			}
		}
		printf("%d
",ans);
	}
#ifdef test
	fclose(stdin);fclose(stdout);system("out.txt");
#endif
	return 0;
}

C

/*
交叉的工作是必须要完成的
而所有的不交叉工作都可以同时完成
那最晚完成的就是交叉次数最多的走廊区域了
注意由于走廊是公用的,所以南北的房间实际上一样
映射到1-200的区域就行了
*/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn=1e3+10;
const int maxm=1e6+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
int cost[maxn];
int main(){
// #define test
#ifdef test
	freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif

	scanf("%d",&casn);
	while(casn--){
		scanf("%d",&n);
		memset(cost,0,sizeof cost);
		for(int i=1;i<=n;i++){
			int x,y;
			scanf("%d%d",&x,&y);
			if(x>y) swap(x,y);
			x=(x>>1)+(x&1);
			y=(y>>1)+(y&1);
			for(int j=x;j<=y;j++){
				cost[j]++;
			}
		}
		int ans=0;
		for(int i=1;i<=400;i++){
			ans=max(cost[i],ans);
		}
		printf("%d
",ans*10);
	}

#ifdef test
	fclose(stdin);fclose(stdout);system("out.txt");
#endif
	return 0;
}

D

/*
有向图的floyd
跑完之后枚举一遍起点
保存最小最长边
输出的时候特判一下
*/
#include <string.h>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int maxn=1e2+10;
const int maxm=1e6+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
int num[maxn];
int dp[maxn][maxn];
int main(){
//#define test
#ifdef test
	freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif

	while(scanf("%d",&n),n){
		memset(dp,INF,sizeof dp);
		for(int i=1;i<=n;i++){
			scanf("%d",&num[i]);
			for(int j=0;j<num[i];j++){
				int a,b;
				scanf("%d%d",&a,&b);
				dp[i][a]=b;
			}
		}
		for(int k=1;k<=n;k++){
			for(int i=1;i<=n;i++){
				for(int j=1;j<=n;j++){
					int t=dp[i][k]+dp[k][j];
					dp[i][j]=min(dp[i][j],t);
				}
			}
		}
		int flag=0,ans=INF,id=0;
		for(int i=1;i<=n;i++){
			int t=0;
			for(int j=1;j<=n;j++){
				if(i==j) continue;
				t=max(t,dp[i][j]);
			}
			if(t<ans) id=i,ans=t;
		}
		if(!id) puts("disjoint");
		else printf("%d %d
",id,ans);
	}
	
#ifdef test
	fclose(stdin);fclose(stdout);system("out.txt");
#endif
	return 0;
}

E

/*
如果i在list里,则进行dfs
如果dfs可以推出失败.则保存答案
dfs:
  状态很显然是20个数字那些不能选
	状态种类不多且每个状态只有2种情况
	考虑状态压缩,每一位保存该位置的数字是否可以使用
	用记忆化搜索,标记2进制所保存的状态的输赢
	不断dfs,换选手,返回下一个选手是输还是赢即可
	如果下一个选手输,则当前为必胜态,以此类推
*/
#include <string.h>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int maxn=2e6+10;
const int maxm=1e6+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
int num[30];
int dp[maxn];
bool vis[30];
int ans[30];
int cps(bool vis[]){
	int stt=0;
	for(int i=2;i<=20;i++){
		stt|=vis[i];
		stt<<=1;
	}
	return stt>>1;
}
bool dfs(int now,bool vis[]){
	bool vis2[30];
	memcpy(vis2,vis,25);
	vis2[now]=false;
	for(int i=2;i+now<=20;i++){
		if(!vis2[i])vis2[i+now]=false;
	}
	int stt=cps(vis2);
	if(dp[stt]){
		return dp[stt]>0;
	}
	for(int i=2;i<=20;i++){
		if(vis2[i]&&!dfs(i,vis2)) return dp[stt]=1;
	}
	dp[stt]=-1;
	return false;
}
int main(){
//#define test
#ifdef test
	freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif
	memset(dp,0,sizeof dp);
	while(scanf("%d",&n),n){
		memset(vis,0,sizeof vis);
		for(int i=1;i<=n;i++){
			scanf("%d",&num[i]);
			vis[num[i]]=true;
		}
		int cnt=0;
		int stt=cps(vis);
		for(int i=2;i<=20;i++){
			if(vis[i]&&!dfs(i,vis)) ans[cnt++]=i;
		}
		printf("Test Case #%d
",++casn);
		if(cnt){
			printf("The winning moves are:");
			for(int i=0;i<cnt;i++){
				printf(" %d",ans[i]);
			}
			puts("");
		}else dp[stt]=-1,puts("There's no winning move.");
		puts("");
	}
	
#ifdef test
	fclose(stdin);fclose(stdout);system("out.txt");
#endif
	return 0;
}

  

原文地址:https://www.cnblogs.com/nervendnig/p/8620950.html