1.照明系统设计——UVa 11400
题目链接:https://vjudge.net/contest/232313#problem/F
思路:既然只能从电压低到电压高替换,那么就相对于电压排序,注意每种电压的灯泡要么全换要么全不换,不然会耗费两种电源的钱。
排序后,设s[i]为前i种灯泡的数量和,d[i]为灯泡1~i的最小花费,则有
d[i]=min{d[j]+(s[i]-s[j])*c[i]+k[i]}
表示前j个用最小开销,第j+1~i个用第i号的电源的最小花销,所以遍历i从1~n,j从0~i,表示遍历前j个开销的最小值。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1005;
struct Node
{
int v,k,c,l;
Node(int _v=0,int _k=0,int _c=0,int _l=0):v(_v),k(_k),c(_c),l(_l){}
bool operator <(const Node &rhs)
{
return v<rhs.v;
}
}node[maxn];
int s[maxn];
int dp[maxn];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
if(n==0)break;
s[0]=0;
for(int i=1;i<=n;i++)
{
int v,k,c,l;
scanf("%d%d%d%d",&v,&k,&c,&l);
node[i]=Node(v,k,c,l);
}
sort(node+1,node+n+1);
for(int i=1;i<=n;i++)
{
s[i]=s[i-1]+node[i].l;
}
dp[0]=0;
for(int i=1;i<=n;i++)
{
dp[i]=INF;
for(int j=0;j<i;j++)
{
dp[i]=min(dp[i],dp[j]+(s[i]-s[j])*node[i].c+node[i].k);
}
}
printf("%d\n",dp[n]);
}
return 0;
}
2.划分为回文串
题目链接:https://vjudge.net/contest/232313#problem/G
思路:用d[i]表示从字符0~i个中可以划分为回文串的最小个数,则d[i]=min{d[j]+1|s[j+1~i]是回文串}。这个转移还是很清晰的,所以需要预处理s[i....j]是否为回文串,时间复杂度为O(n^2),状态O(n)个,决策O(n)个,时间复杂度为O(n^2)。
(其实好像是常数太大了吧,O(n^3)更快一些。)
代码一:O(n^3)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1010;
char c[maxn];
int dp[maxn];
bool ishuiwen(int l,int r)
{
while(l<r)
{
if(c[l]!=c[r])return false;
l++;
r--;
}
return true;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",c+1);
int len=strlen(c+1);
dp[0]=0;
for(int i=1;i<=len;i++)
{
dp[i]=i+1;
for(int j=1;j<=i;j++)
{
if(ishuiwen(j,i))
{
dp[i]=min(dp[i],dp[j-1]+1);
}
}
}
printf("%d\n",dp[len]);
}
return 0;
}
代码二:O(n^3)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <string>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
char s[1010];
int f[1010][1010], dp[1010];
int x, y, len;
void DP()
{
int flag = 1;
for (int i = x, j = y; i >= 1 && j <= len; i--, j++){
if (flag){
if (s[i] == s[j]) f[i][j] = 1;
else f[i][j] = INF, flag = 0;
}
else f[i][j] = INF;
}
}
int main()
{
dp[1] = 1;
int t;
scanf("%d", &t);
while (t--){
scanf("%s", s+1);
len = strlen(s+1);
f[len][len] = 1;
for (x = 1; x < len; x++)
for (y = x; y <= x+1; y++) DP();
for (int i = 2; i <= len; i++){
dp[i] = INF;
for (int j = 0; j < i; j++) dp[i] = min(dp[i], dp[j]+f[j+1][i]);//判断的是从j+1到i是不是回文
}
printf("%d\n", dp[len]);
}
return 0;
}
3.牛客小白月赛7——B.自杀游戏
题目链接:https://www.nowcoder.com/acm/contest/190/B
思路:组合游戏DP,注意两点状态的变换:
1.必败态之后就是必胜态
2.因为每个人都可以将时间加快[a,b]的任意时间,那么如果在t-(a+1)~t-(b+1)的时间范围内,如果有必败态,即dp[j]=0,都可以通过通过调快时间使dp[i]为必胜态,理解了这一点这个题目就好做了。具体细节看代码。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn=1e5+10;
int dp[maxn];
int main()
{
int a,b,n;
while(scanf("%d%d%d",&n,&a,&b)!=EOF)
{
//memset(dp,0,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++)
{
if(dp[i-1]==0)
{
dp[i]=1;//必败态之后为必胜态
}
else
{
if(i<a+1)//不能调,必胜态之后为必败态
dp[i]=0;
for(int j=a+1;j<=b+1;j++)//可调时间范围
{
if(i>=j&&dp[i-j]==0)//如果在t-(a+1)~t-(b+1)的时间段内有必败态,都可以通过调整时间成为必胜态。
{
dp[i]=1;
break;
}
}
if(dp[i]!=1)dp[i]=0;
}
}
if(dp[n]==1)
{
puts("Alice");
}
else
{
puts("Bob");
}
}
return 0;
}